为什么LLVM中的Expected<T>要为Expected<T>&&实现两个构造函数?

Why does Expected<T> in LLVM implement two constructors for Expected<T>&&?

Expected<T> 在 llvm/Support/Error.h 中实现。它是一个标记的联合,持有 TError.

Expected<T> 是模板 class,类型为 T:

template <class T> class LLVM_NODISCARD Expected

但这两个构造函数真的让我很困惑:

  /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT
  /// must be convertible to T.
  template <class OtherT>
  Expected(Expected<OtherT> &&Other,
           typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
               * = nullptr) {
    moveConstruct(std::move(Other));
  }

  /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT
  /// isn't convertible to T.
  template <class OtherT>
  explicit Expected(
      Expected<OtherT> &&Other,
      typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
          nullptr) {
    moveConstruct(std::move(Other));
  }

为什么 Expected<T> 为同一个实现重复两个结构?为什么不这样做呢?:

template <class OtherT>
Expected(Expected<OtherT>&& Other) { moveConstruct(std::move(Other));}

要理解这一点,我们应该从 std::is_convertible 开始。根据 cppreference:

If the imaginary function definition To test() { return std::declval<From>(); } is well-formed, (that is, either std::declval<From>() can be converted to To using implicit conversions, or both From and To are possibly cv-qualified void), provides the member constant value equal to true. Otherwise value is false. For the purposes of this check, the use of std::declval in the return statement is not considered an odr-use.

Access checks are performed as if from a context unrelated to either type. Only the validity of the immediate context of the expression in the return statement (including conversions to the return type) is considered.

这里的重要部分是它只检查隐式转换。因此,您的 OP 中的两个实现意味着如果 OtherT 可隐式转换为 T,则 expected<OtherT> 可隐式转换为 expected<T>。如果 OtherT 需要显式转换为 T,则 Expected<OtherT> 需要显式转换为 Expected<T>.

以下是隐式和显式转换及其 Expected 对应物的示例

int x;
long int y = x;              // implicit cast ok
Expected<int> ex;
Expected<long int> ey = ex;  // also ok

void* v_ptr;
int* i_ptr = static_cast<int*>(v_ptr);              // explicit cast required
Expected<void*> ev_ptr;
auto ei_ptr = static_cast<Expected<int*>>(ev_ptr);  // also required

因为根据提案,构造函数是条件显式。这意味着构造函数 只有在满足某些条件 时才显式(这里是 TOtherT 的可转换性)。

C++ 在 C++20 之前没有此功能的机制(类似于 explicit(condition))。因此,实现需要使用一些其他机制,例如 定义两个不同的构造函数 — 一个 explicit 和另一个 converting — 并确保根据条件选择合适的构造函数。这通常是在 std::enable_if 的帮助下通过 SFINAE 完成的,条件已解决。


从 C++20 开始,应该有 explicit 说明符的条件版本。使用单个定义实现起来会容易得多:

template <class OtherT>
explicit(!std::is_convertible_v<OtherT, T>)
Expected(Expected<OtherT> &&Other)
{
   moveConstruct(std::move(Other));
}