Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

llvm

Expected<T> is implemented in llvm/Support/Error.h. It is a tagged union holding either a T or an Error.

Expected<T> is a template class with type T:

template <class T> class LLVM_NODISCARD Expected

But these two constructors really confuse me:

  /// 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));
  }

Why does Expected<T> repeat two constructs for the same implementation? Why doesn't it do it like this?:

template <class OtherT>
Expected(Expected<OtherT>&& Other) { moveConstruct(std::move(Other));}
like image 438
yodahaji Avatar asked Oct 20 '19 08:10

yodahaji


1 Answers

Because that constructor is conditionally explicit according to the proposal. This means that the constructor is explicit only if some condition is met (here, convertibility of T and OtherT).

C++ does not have a mechanism for this functionality (something as explicit(condition)) before C++20. Implementations thus need to use some other mechanism, such as a definition of two different constructors — one explicit and another one converting — and ensure the selection of the proper constructor according to the condition. This is typically done via SFINAE with the help of std::enable_if, where the condition is resolved.


Since C++20, there should be a conditional version of the explicit specifier. The implementation then would be much easier with a single definition:

template <class OtherT>
explicit(!std::is_convertible_v<OtherT, T>)
Expected(Expected<OtherT> &&Other)
{
   moveConstruct(std::move(Other));
}
like image 105
Daniel Langr Avatar answered Sep 23 '22 02:09

Daniel Langr