2012-04-29 8 views
8

テンプレートクラスの特別なメンバー関数(具体的には、コピー/移動コンストラクタやコピー/移動代入演算子)がインスタンス化されるのはいつですか?クラス自体がインスタンス化されるとすぐに、または必要なときにのみ、テンプレートクラスの特別なメンバー関数がインスタンス化されるのはいつですか?

これは、次のような状況で起動します:

test.cpp:9:5: error: the parameter for this explicitly-defaulted copy constructor is const, but a member or base requires it to be 
     non-const 
    pair(const pair&) = default; 
    ^
test.cpp:21:18: note: in instantiation of template class 'pair<int, S>' requested here 
    pair<int, S> p; 
       ^

それはできるだけ早くコピーコンストラクタをインスタンス化しようとすることを示唆している:

template <class T, class U> 
struct pair 
{ 
    T first;     
    U second;     

    pair() : first(), second() {} 

    pair(const pair&) = default; 
}; 

struct S 
{ 
    S() {} 
    S(const S&) = delete; 
    S(S&&) = default; 
}; 

int main() 
{ 
    pair<int, S> p; 
} 

クランは、次のエラーで、このコードをコンパイルすることを拒否しますクラスがインスタンス化されます。

しかし、GCCはコードをうまくコンパイルして、実際に必要な場合にのみコピーコンストラクタをインスタンス化しようとします。

どのコンパイラの動作が正しいですか?

(同様の不一致が代入演算子のために展示されています。)

UPDATE:私は、その定義を変更した場合ので、これは、この例ではpairのコピーコンストラクタがdefault EDであるという事実とは何かを持っています〜

pair(const pair& p) : first(p.first), second(p.second) {} 

このコードはclangも渡します。

+0

-std = C++ 11でclang3.0とコンパイルしてください。 –

答えて

2

規格の関連通路は[dcl.fct.def.default]/1:

明示的に初期設定され機能が なければならない[...] は、(同一の宣言された関数型を有しますコピーコンストラクタやコピー代入演算子の場合、パラメータタイプは "non-const Tへの参照"(Tはメンバ関数のクラスの名前)である場合を除いて、ref-qualifierが異なる場合を除き、暗黙に宣言されていた

このルールは、デフォルトの機能が使用されない場合でも適用されます。さて、[class.copy]/9は述べています:

[...]すべての非静的データメンバーの場合は、暗黙的に宣言されたコピーコンストラクタが

X::X(const X&)

形式になります。クラスタイプM [...]のXである場合、そのような各クラスタイプは、最初のパラメータがタイプconst M&またはconst volatile M&のコピーコンストラクタを持ちます。

そうでない場合は、暗黙的に宣言されたコピーコンストラクタは

X::X(X&)

フォームがしたがって、このような例は、病気-形成されている(そして、あなたが見ている診断生成する必要があります)があります:

struct A { 
    A(); 
    A(A&); // Note, non-const type A in copy constructor 
}; 
template<typename T> 
struct B { 
    T t; 
    B(); 
    B(const B&) = default; 
}; 
B<A> b; // error, B<A>::B(const B&) is defaulted but has the wrong type 

ただし、この例ではこのルールは適用されません。 clangバグ(これは既に修正済み)のため、削除されたコピーコンストラクタは非constパラメータ型を持つと誤って認識され、このエラーが発生します。

5

現在のC++ 11標準のセクション14.7.1をご覧ください。ドラフトのn3242版から引用すると:

クラステンプレートの特殊化の暗黙のインスタンス化は、宣言のではなく、 定義やデフォルト引数の、クラスのメンバ関数の 暗黙のインスタンス生成の原因となる メンバークラス、静的データメンバーおよびメンバーテンプレート。 は、メンバ 匿名ユニオンの定義の暗黙的なインスタンス化を引き起こします。クラステンプレートのメンバまたはメンバ テンプレートが明示的にインスタンス化されているか、または明示的に特殊化されていない限り、 定義が存在する必要があるコンテキストで 特殊化が参照されると、メンバの特殊化が暗黙的にインスタンス化されます。 静的データメンバ自体が、静的データメンバの定義 の定義を必要とする方法で使用されている場合を除いて、静的データメンバの初期化(およびいずれの 副作用も発生しません)が発生しません。

したがって、上記のようにクラスを型として使用すると、宣言だけがそのインスタンスでインスタンス化されます。コピーコンストラクタの実際の(デフォルトの)実装は、上記のコードでは必要ないので、インスタンス化するべきではありません。 GCCはこれを正しく処理していますが、Clangはこれを処理していません。

また、あなたの編集では、直接実装されたコピーコンストラクタにも障害があるため、デフォルトのコピーコンストラクタの実装が早すぎることが示唆されています(コピーコンストラクタをSとして呼び出すことはできません。 )。既定の実装と実装はすべての点で(インスタンス化の時刻も含めて)同じである必要があるので、これは不名誉なバグであると考えています。

関連する問題