Expected<T>
在llvm / Support / Error.h中实现。这是一个带有T
或Error
的带标记的联合。
Expected<T>
是类型为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));}
答案 0 :(得分:8)
因为根据建议,构造函数是有条件显式的。这意味着,只有满足某些条件(此处是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
和T
的可转换性)时,构造函数才显式。
C ++在C ++ 20之前没有用于此功能的机制(诸如OtherT
)。因此,实现需要使用其他机制,例如定义两个不同的构造函数-一个 explicit 和另一个 converting -并确保选择根据条件选择适当的构造函数。这通常是通过SFINAE在explicit(condition)
的帮助下完成的,条件已解决。
从C ++ 20开始,应该有一个std::enable_if
说明符的条件版本。只需一个定义,实施起来就会容易得多:
explicit
答案 1 :(得分:5)
要理解这一点,我们应该从std::is_convertible
开始。根据{{3}}:
如果虚函数定义
To test() { return std::declval<From>(); }
的格式正确,(即std::declval<From>()
可以使用隐式转换转换为To
,或者From
和{ {1}}可能是cv限定的void),并提供等于To
的成员常量值。否则,值为true
。为了进行此检查,在return语句中使用false
不被认为是odr-use。执行访问检查就像是从与两种类型都不相关的上下文中进行。仅考虑return语句中表达式的即时上下文的有效性(包括对返回类型的转换)。
这里的重要部分是它仅检查隐式转换。因此,OP中的两种实现意味着:如果std::declval
隐式可转换为OtherT
,那么T
隐式可转换为expected<OtherT>
。如果expected<T>
要求显式转换为OtherT
,则T
要求并显式转换为Expected<OtherT>
。
以下是隐式和显式强制转换及其Expected<T>
对应示例
Expected