在 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();
}