Skip to content

Instantly share code, notes, and snippets.

@yllan
Last active February 17, 2021 04:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yllan/abe223009837f30bdbaa08e7ab8b9b19 to your computer and use it in GitHub Desktop.
Save yllan/abe223009837f30bdbaa08e7ab8b9b19 to your computer and use it in GitHub Desktop.
std::variant

std::variant 是 C++ 的 sum type

在 C++17 後要 sum type 可以這樣用:

#include<variant>

// data Solution = Surface Vector3 Double |
//                 Line Vector3 Vector3 |
//                 Point Vector3 |
//                 EmptySet
using Solution = std::variant<Surface, Line, Point, EmptySet>;

// overload for each type
Solution intersect(const Surface& s, const Surface& t) { /* ... */ }
Solution intersect(const Surface& s, const Line& l) { /* ... */ }
Solution intersect(const Surface& s, const Point& p) { /* ... */ }
Solution intersect(const Surface& s, const EmptySet& e) { /* ... */ }

拿到 variant,除了已知型態可以用各種 get<> 來取值,幾乎什麼也不能做。要處理未知內容型態的 variant,必須使用 std::visit。最簡單的就是用 auto:

string to_string(Solution s) {
  return visit([](const auto x) { return to_string(x); }, s);
}

但如果真的要對 type 做 pattern matching,就要先寫以下兩行 utility 將你的 closure 轉成 C++ function object。

template<class... Ts> struct overload : Ts... { using Ts::operator()...; };
template<class... Ts> overload(Ts...) -> overload<Ts...>;

然後就可以:

bool valid = visit(overload {
  [&](const Surface &s) { /* ... */ },
  [&](const Line &l) { /* ... */ },
  [&](const Point &p) { /* ... */ },
  [&](const EmptySet &e) { /* ... */ }
}, sol);

注意

雖然 return type 是 variant,但不可用 C++ ternary operator。

  return overlap ? Line(o, v) : EmptySet(); // compile-error

要寫成 if-else

// okay
if (overlap) {
  return Line(o, v);
} else {
  return EmptySet();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment