web-dev-qa-db-ja.com

2つの引数を持つdecltypeとは何ですか?

混乱を避けるために編集:decltype2つの引数を受け入れません。回答を参照してください。

次の2つの構造体を使用して、コンパイル時に型Tにメンバー関数が存在するかどうかを確認できます。

_// Non-templated helper struct:
struct _test_has_foo {
    template<class T>
    static auto test(T* p) -> decltype(p->foo(), std::true_type());

    template<class>
    static auto test(...) -> std::false_type;
};

// Templated actual struct:
template<class T>
struct has_foo : decltype(_test_has_foo::test<T>(0))
{};
_

メンバー関数の存在を確認するときにSFINAEを使用するという考えなので、p->foo()が無効な場合は、testの省略記号バージョンのみが_std::false_type_が定義されています。それ以外の場合、最初のメソッドは_T*_に対して定義され、_std::true_type_を返します。実際の「切り替え」は、testによって返される型から継承する2番目のクラスで発生します。これは、_is_same_やそのようなものを使用したさまざまなアプローチと比較して、賢く「軽量」のようです。

2つの引数を持つdecltypeは、式のタイプを取得するだけだと思っていたので、最初は驚きました。上記のコードを見たとき、「式をコンパイルして、常に秒の型を返すようにしてください。式のコンパイルに失敗した場合は失敗します」のようなものだと思いました(したがって、この特殊化を非表示にします; SFINAE)。

だが:

次に、このメソッドを使用して、何らかのタイプTに依存する限り、「isvalidexpression」チェッカーを記述できると思いました。例:

_...
    template<class T>
    static auto test(T* p) -> decltype(bar(*p), std::true_type());
...
_

http://ideone.com/dJkLPF

これは、barが最初のパラメーターとしてTを受け入れて定義されている場合(またはTが変換可能である場合)にのみ、_std::true_type_を返すと思いました。など)、つまり、pがタイプ_T*_で定義されているコンテキストで記述された場合、bar(*p)がコンパイルされます。

ただし、上記の変更では、常にが_std::false_type_に評価されます。 これはなぜですか?複雑な別のコードで修正したくありません。思ったように動かない理由を知りたいだけです。明らかに、decltypeと2つの引数は、私が思っていたものとは異なる動作をします。ドキュメントが見つかりませんでした。それはどこでも1つの表現でのみ説明されます。

45
leemes

これはコンマで区切られた式のリストであり、タイプはリストの最後の式のタイプと同じです。これは通常、first式が有効であることを確認するために使用され(コンパイル可能、SFINAEと考えてください)、2番目は、最初の式が有効な場合にdecltypeが返されるように指定するために使用されます。

38
Daniel Frey

decltypeは2つの引数を取りません。簡単に言うと、引数として式を使用できます。コンマ演算子は、式を作成する1つの方法です。 5.18/1項による:

[...]コンマで区切られた式のペアは、左から右に評価されます。左の式は破棄された値の式です(第5節)。左の式に関連するすべての値の計算と副作用は、右の式に関連するすべての値の計算と副作用の前にシーケンスされます。 結果の型と値は右オペランドの型と値です;結果は、右のオペランドと同じ値のカテゴリになり、右のオペランドがglvalueとビットフィールドの場合はビットフィールドになります。右のオペランドの値が一時(12.2)の場合、結果はその一時です。

したがって:

static_assert(std::is_same<decltype(42, 3.14), double>::value, "Will not fire");
14
Andy Prowl