为什么LLVM中的Expected<T>要为Expected<T>&&实现两个构造函数?
Why does Expected<T> in LLVM implement two constructors for Expected<T>&&?
Expected<T>
在 llvm/Support/Error.h 中实现。它是一个标记的联合,持有 T
或 Error
.
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
因为根据提案,构造函数是条件显式。这意味着构造函数 只有在满足某些条件 时才显式(这里是 T
和 OtherT
的可转换性)。
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));
}
Expected<T>
在 llvm/Support/Error.h 中实现。它是一个标记的联合,持有 T
或 Error
.
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, eitherstd::declval<From>()
can be converted toTo
using implicit conversions, or bothFrom
andTo
are possibly cv-qualified void), provides the member constant value equal totrue
. Otherwise value isfalse
. For the purposes of this check, the use ofstd::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
因为根据提案,构造函数是条件显式。这意味着构造函数 只有在满足某些条件 时才显式(这里是 T
和 OtherT
的可转换性)。
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));
}