自己设计 Result/Either:Success 类型和 Error 类型应该始终不同吗?

Designing own Result/Either: Should Success type and Error type always be destinct?

我有一个用于 C++ 的 Result/Either 的实现。 class 功能遵循 type-converting constructor

template <typename V, typename E>
class Result {
...
    template<typename X>
    constexpr Result(X&& value);
...
};

这样做的目的是启用这样的代码:

Result<SomeType, int> value = SomeType{...};
EXPECT_TRUE(result.isOk());   // TRUE

// Also
Result<SomeType, int> value = 10;
EXPECT_TRUE(result.isError());   // TRUE

这对于函数 returning 值很重要:

Result<SuccessType, int> func() {
  SuccessType result{...};
...
  return result;
}
...

EXPECT_TRUE(func().isOk());

现在事实证明,这样的构造函数过于宽松,会导致不受欢迎的类型conversion.s我想对其进行约束。

所以问题是: - 你认为允许相同类型的值结果和错误有用吗?例如: Result<MyTypeA, MyTypeA> r{};有没有错误类型和成功类型相同的例子有用?

这个问题的动机是我可以摆脱 template<> constructor 但前提是 V 和 E 不是同一类型:

template<class V, class E>
struct Result {
    constexpr result(V&& value);    
    constexpr result(E&& value);
...

如果您是 对称 要么您想要从任何一种类型隐式转换。如果你通过了可以转换为两者的东西,你想失败。

如果您也是 非对称 类型,就像结果一样,您想隐式地从 favored 类型转换。对于不受欢迎的类型,您只想显式转换。

template<class X>
struct Error {
  X x;
};

template<class V, class E>
struct Result {
  Result( V&& v );
  Result( Error<E> e );
  // or even:
  template<class O,
    std::enable_if_t< std::is_convertible_v<O, E>, bool > = true
  >
  Result( Error<O> e );
};

使用看起来像:

Result<SomeType, int> value = SomeType{...};
EXPECT_TRUE(result.isOk());   // TRUE

// Also
Result<SomeType, int> value = Error{10};
EXPECT_TRUE(result.isError());   // TRUE

并且:

Result<SuccessType, int> func() {
  SuccessType result{...};
...
  return result;
} 

有效,如下:

Result<SuccessType, int> func() {
  return Error{10};
} 

应避免隐式转换为错误状态,意外访问太容易了。