どうやら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種類のイテレータ:iterator
sとconst_iterator
s。これら2つのコードを複製することは意味がないため、元のクラスへのポインターを取得して、テンプレートイテレータークラスを作成できますMyContainer<T> *
またはconstバージョンへのポインタMyContainer<T> const *
。
cbegin
とcend
を一緒に使用すると、gccがエラーになり、競合するタイプが推測されたと表示されますが、clangは正常に機能します。
さて、これが私が標準( 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&&
_です。ここで、T
はe
のタイプです。 ;
—それ以外の場合、e
が左辺値の場合、decltype(e)
は_T&
_です。ここで、T
はe
のタイプです。 ;
—それ以外の場合、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が間違っているように見えます。