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

yod*_*aji 8 c++ llvm

Expected<T>在llvm / Support / Error.h中实现。这是一个标记的并集,包含a T或a Error

Expected<T>是类型为的模板类T

template <class T> class LLVM_NODISCARD Expected
Run Code Online (Sandbox Code Playgroud)

但是,这两个构造函数确实使我感到困惑:

  /// 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));
  }
Run Code Online (Sandbox Code Playgroud)

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

template <class OtherT>
Expected(Expected<OtherT>&& Other) { moveConstruct(std::move(Other));}
Run Code Online (Sandbox Code Playgroud)

Dan*_*ica 8

因为根据建议,该构造函数在条件上显式的。这意味着仅当满足某些条件(此处为T和的可转换性OtherT)时,构造函数才是显式的

explicit(condition)在C ++ 20之前,C ++ 没有针对此功能的机制(诸如)。因此,实现需要使用其他机制,例如定义两个不同的构造函数(一个显式,另一个进行转换),并确保根据条件选择适当的构造函数。通常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));
}
Run Code Online (Sandbox Code Playgroud)


pat*_*gan 5

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

如果虚函数定义To test() { return std::declval<From>(); }是良好的,(即,或者std::declval<From>()可以被转换为To使用隐式转换,或两者FromTo是可能的CV-合格空隙),提供了构件恒定值以下的true。否则,值为false。为了进行此检查,std::declval在return语句中使用不被视为odr-use。

就像从与这两种类型都不相关的上下文中一样执行访问检查。仅考虑return语句中表达式的即时上下文的有效性(包括对返回类型的转换)。

这里的重要部分是它仅检查隐式转换。因此,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
Run Code Online (Sandbox Code Playgroud)