LLVMのExpected <T>がExpected <T> &&の2つのコンストラクターを実装するのはなぜですか?


8

Expected<T>llvm / Support / Error.hに実装されています。a Tまたはのいずれかを保持するタグ付きユニオンErrorです。

Expected<T>タイプのテンプレートクラスTです:

template <class T> class LLVM_NODISCARD Expected

しかし、これらの2つのコンストラクターは本当に私を混乱させます:

  /// 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>同じ実装に対して2つの構成を繰り返すのはなぜですか?なぜこのようにしないのですか?:

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

1
explicitキーワードに注意してください
Mat

explicitここでキーワードが重要な理由を知りたいですか?誰かが例を挙げられますか?
yodahaji

回答:


8

そのコンストラクターは、提案に従って条件付きで明示的であるためです。これは、ある条件が満たされた場合にのみコンストラクタが明示的であることを意味します(ここではTOtherT)。

C ++には、explicit(condition)C ++ 20より前のこの機能(のようなもの)のメカニズムはありません。したがって、実装では、2つの異なるコンストラクター(1つは明示的、もう1つは変換)の定義など、他のメカニズムを使用し、条件に従って適切なコンストラクターを選択する必要があります。これは通常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));
}

ご回答有難うございます。私はまだ混乱していますが、「条件付きで明示的」にググリングした後、多くのリソースを取得しました。
yodahaji

@yodahaji 条件付き明示は標準的な用語ではないことに注意してください。それは単に、コンストラクタが明示的であるか、またはある条件に従って変換していることを意味します。
Daniel Langr

5

これを理解するには、まずから始めstd::is_convertibleます。cppreferenceによると:

虚数関数の定義 To test() { return std::declval<From>(); }が整形式である場合(つまり、暗黙の変換std::declval<From>()To使用して変換できるか、またはその両方FromToあり、cv修飾されたvoidである可能性がある場合)は、に等しいメンバー定数値を提供しtrueます。それ以外の場合、値はfalseです。このチェックのために、std::declvalは、returnステートメントでの、odr-useとは見なされません。

アクセスチェックは、どちらのタイプにも関連しないコンテキストからの場合と同様に実行されます。returnステートメント内の式の直接のコンテキスト(戻り値の型への変換を含む)の妥当性のみが考慮されます。

ここで重要なのは、暗黙的な変換のみをチェックすることです。したがって、OPの2つの実装の意味は、OtherTが暗黙的にに変換可能な場合Texpected<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

ご回答有難うございます。しかし、「意味のExpected<OtherT>ある明示的なキャストが必要」の意味がわかりませんExpected<T>。ここでの「明示的なキャスト」とはどういう意味ですか?この例は想像できません。
yodahaji

明示的なキャストの意味を明確にするために、いくつかの例を投稿に追加しました。一般に、バグを引き起こす可能性のある偶発的な暗黙的キャストを防ぐために使用されます。残念ながら、現在コードをテストすることはできないので、タイプミスやバグを見つけた場合はお知らせください。修正します。
patatahooligan

この文は、「偶発的な暗黙のキャストを防ぐために」私の質問に答えます。ありがとう:)
ヨーダハジ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.