在工作中,我遇到了一种情况,描述函数返回结果的最佳类型是 std::variant<uint64_t, uint64_t>
- 当然,这不是有效的 C++,因为你不能有两个相同类型的变体。我可以将其表示为 std::pair<bool, uint64_t>
, 或者该对的第一个元素是枚举,但这是一种特殊情况; std::variant<uint64_t, uint64_t, bool>
不太清楚,而且我的函数式编程背景真的让我想要 Either
- 所以我去尝试实现它,使用访问者模式,因为我已经能够在没有对 sum 类型的本地支持的情况下在其他语言中做到这一点:
template <typename A, typename B, typename C>
class EitherVisitor {
virtual C onLeft(const A& left) = 0;
virtual C onRight(const B& right) = 0;
};
template <typename A, typename B>
class Either {
template <typename C>
virtual C Accept(EitherVisitor<A, B, C> visitor) = 0;
};
template <typename A, typename B>
class Left: Either<A, B> {
private:
A value;
public:
Left(const A& valueIn): value(valueIn) {}
template <typename C>
virtual C Accept(EitherVisitor<A, B, C> visitor) {
return visitor.onLeft(value);
}
};
template <typename A, typename B>
class Right: Either<A, B> {
private:
B value;
public:
Right(const B& valueIn): value(valueIn) {}
template <typename C>
virtual C Accept(EitherVisitor<A, B, C> visitor) {
return visitor.onRight(value);
}
};
C++ 拒绝这个,因为模板方法 Accept
不能是虚拟的。是否有解决此限制的方法,这将允许我根据其 f 代数和变形来正确表示基本和类型?
最佳答案
也许最简单的解决方案是围绕 T
使用轻量级包装器。为 Right
和 Left
?
基本上是一个强类型别名(也可以使用 Boost 的 strong typedef )
template<class T>
struct Left
{
T val;
};
template<class T>
struct Right
{
T val;
};
然后我们可以区分它们进行访问:template<class T, class U>
using Either = std::variant<Left<T>, Right<U>>;
Either<int, int> TrySomething()
{
if (rand() % 2 == 0) // get off my case about rand(), I know it's bad
return Left<int>{0};
else
return Right<int>{0};
}
struct visitor
{
template<class T>
void operator()(const Left<T>& val_wrapper)
{
std::cout << "Success! Value is: " << val_wrapper.val << std::endl;
}
template<class T>
void operator()(const Right<T>& val_wrapper)
{
std::cout << "Failure! Value is: " << val_wrapper.val << std::endl;
}
};
int main()
{
visitor v;
for (size_t i = 0; i < 10; ++i)
{
auto res = TrySomething();
std::visit(v, res);
}
}
Demo
关于c++ - C++ 中的求和类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64779163/