web-dev-qa-db-ja.com

C ++のdecltype(this)のタイプは何ですか?

どうやらclangはdecltype(this)がcv修飾クラスへのポインタであると考えているのに対し、gccはそれがcv修飾クラスへのポインタへのconst referenceであると考えています。 GCCは、decltype(&*this)がcv修飾クラスへのポインターであるとのみ見なします。これは、テンプレートの型名として使用される場合にいくつかの影響があります。架空の例を考えてみましょう。

template<typename T>
class MyContainer {
    /* ... */
    template<typename ContainerPtr>
    class MyIterator {
        ContainerPtr container;
        /* ... */
    };
    auto cbegin() const
        -> MyIterator<decltype(&*this)> { return { /* ... */ }; }
    auto cend() const
        -> MyIterator<decltype(this)> { return { /* ... */ }; }
};

この例では、Tのカスタムコンテナを実装しています。コンテナであるため、イテレータをサポートします。実際、2種類のイテレータ:iteratorsとconst_iterators。これら2つのコードを複製することは意味がないため、元のクラスへのポインターを取得して、テンプレートイテレータークラスを作成できますMyContainer<T> *またはconstバージョンへのポインタMyContainer<T> const *

cbegincendを一緒に使用すると、gccがエラーになり、競合するタイプが推測されたと表示されますが、clangは正常に機能します。

28
kccqzy

さて、これが私が標準( N3337 )で見つけたものですが:

7.1.6.2単純型指定子[dcl.type.simple]

4 decltype(e)で示されるタイプは次のように定義されます。
eが括弧なしの場合 id-expression または、括弧で囲まれていないクラスメンバーアクセス(5.2.5)の場合、decltype(e)eで指定されたエンティティのタイプです。そのようなエンティティがない場合、またはeがオーバーロードされた関数のセットに名前を付けている場合、プログラムの形式は正しくありません。
—それ以外の場合、eがxvalueの場合、decltype(e)は_T&&_です。ここで、Teのタイプです。 ;
—それ以外の場合、eが左辺値の場合、decltype(e)は_T&_です。ここで、Teのタイプです。 ;
—それ以外の場合、decltype(e)eのタイプです。
decltype指定子のオペランドは未評価のオペランドです(第5節)。

そして

5.1.1一般[expr.prim.general]

3宣言がクラスXのメンバー関数またはメンバー関数テンプレートを宣言している場合、式thisは「ポインタ」タイプのprvalueです。 cv-qualifier-seq オプションの間にX” cv-qualifer-seq との終わり 関数定義、 メンバー宣言者、または 宣言者。オプションの前に表示してはならない cv-qualifier-seq また、静的メンバー関数の宣言内に表示されないものとします(ただし、そのタイプと値のカテゴリーは、非静的メンバー関数内にあるため、静的メンバー関数内で定義されます)。 [ 注意: これは、完全な宣言者がわかるまで宣言の一致が発生しないためです。 — エンドノート ]他のコンテキストのオブジェクト式とは異なり、_*this_は、メンバー関数本体の外部でクラスメンバーアクセス(5.2.5)を行うために、完全な型である必要はありません。 [ 注意: 宣言の前に宣言されたクラスメンバーのみが表示されます。 — エンドノート ]

§9.3.2への以前の参照はエラーです。これは、以下で指摘するように、メンバー関数のbodyを扱っているためです in MWidによるコメント

9.3.2`this`ポインタ[class.this] 1非静的(9.3)メンバー関数の本体では、キーワード `this`は、関数が呼び出されるオブジェクトのアドレスを値とするprvalue式です。クラス `X`のメンバー関数の` this`の型は `X *`です。メンバー関数が `const`と宣言されている場合、` this`のタイプは `const X *`であり、メンバー関数が `volatile`と宣言されている場合、` this`のタイプは `volatile X *`であり、メンバーの場合関数は `const volatile`として宣言され、` this`のタイプは `const volatile X *`です。

したがって、gccが間違っているように見えます。

17
kccqzy

thisprvalueであるため、decltype(this)は常にプレーンなX*(またはX cv*/cv X*)である必要があります。 const&の追加は、GCC(g ++ 4.8.1でテスト済み)のバグのようです。これは、クラスtemplate( "plain"クラスではない)でのみ発生します- 末尾の戻り値の型の内部(メンバー関数の本体の内部ではありません): デモ 。これはGCC4.9(実験的)で修正されているようです。テストできます ここ

14
gx_