2012-03-07 7 views
7

テンプレート定義がテンプレート定義とどのくらい正確に一致していますか?私は標準でいくつかのテキストを見つけましたテンプレートのID "テンプレート名 [...]は同じテンプレートと[...]"(14.4 [temp.type] p1)しかしテンプレート名の定義が見つからないまたはテンプレート名が同じテンプレートを参照しています。とにかく文法をよく解読していないので、のテンプレートIDがテンプレートの定義/宣言の一部であるか、またはテンプレート。テンプレート定義とテンプレート宣言はどのように一致していますか?

たとえば、次のプログラムは正常に動作します。

#include <iostream> 

template<typename T> 
T foo(T t); 

int main() { 
    foo(1); 
} 

template<typename T> 
T foo(T t) 
{ std::cout << "A\n"; return 0; } 

テンプレート定義でテンプレートパラメータを使用する方法を変更すると、その名前は明らかに同じテンプレートを参照しなくなり、リンクは失敗します。私はC++(MSVC 11ベータ版)の私の実装のために、別の翻訳単位にテンプレート定義を移動した場合

#include <iostream> 

template<typename T> 
T foo(T t); 

int main() { 
    foo(1); 
} 

template<typename T> 
int foo(T t) { std::cout << "A\n"; return 0; } 

// or 

template<typename T> 
struct identity { 
    typedef T type; 
}; 

template<typename T> 
typename identity<T>::type 
foo(T t) { std::cout << "A\n"; return 0; } 

は次に、プログラムは関係なく、私は種類が言うどのように動作しません。

//main.cpp 

template<typename T> 
T foo(T t); 

int main() { 
    foo(1); 
} 

//definition.cpp 
#include <iostream> 

template<typename T> 
struct identity { 
    typedef T type; 
}; 

template<typename T> 
typename identity<T>::type 
foo(T t) { std::cout << "A\n"; return 0; } 

template int foo<int>(int); 

または

//definition.cpp 
#include <iostream> 

template<typename T> 
int foo(T t) { std::cout << "A\n"; return 0; } 

template int foo<int>(int); 

や定義がまったくテンプレートがない場合でも:明らかに署名/マングルされた名前が同じであるため、成功しているリンク

//definition.cpp 
#include <iostream> 

int foo(T t) { std::cout << "A\n"; return 0; } 

かかわらず、テンプレートをインスタンス化してシンボルを作成します。

§14.1 [TEMP]を

P6関数テンプレート、クラステンプレートのメンバ関数、またはクラステンプレートの静的 データメンバをしなければならない。私は違反してるので、私はこの未定義の動作を考えます 対応する特殊化が明示的にインスタンス化されていない限り(14.7.2)、 一部の翻訳単位で暗黙的にインスタンス化される単位(14.7.1)で定義されます。診断は必要ありません。

しかし、その後、私は第2の翻訳単位でテンプレートの定義を入れ、そして2つのいずれかの場所での明示的なインスタンスを含むことにより、これらの要求を満たすためにしようと言う:

#include <iostream> 

template<typename T> 
T foo(T t) { std::cout << "A\n"; return 0; } 

// Location 1  

template<typename T> 
int foo(int t) { std::cout << "B\n"; return 0; } 

// Location 2 

ルールは何ですか明示的なインスタンシエーションが参照するテンプレートの曖昧さについては?場所1に置くと正しいテンプレートがインスタンス化され、その定義が最終プログラムで使用され、場所2に置くともう一方のテンプレートがインスタンス化され、上の14.1 p6で定義されていない動作が発生すると考えられます。これは来た

#include <iostream> 

template<typename T> 
T foo(T t) { std::cout << "A\n"; return 0; } 

template<typename T> 
int foo(int t) { std::cout << "B\n"; return 0; } 

int main() { 
    foo(1); // prints "A" 
} 

理由:それはテンプレートの曖昧さを解消するためのルールのように思えるので、一方

2つのテンプレート定義の暗黙のインスタンス生成は、このような状況が異なっている、どんなの最初のテンプレートを選びません

0123:アップは、質問者は、単一の前方宣言

template<typename T> 
T CastScriptVarConst(const ScriptVar_t& s); 

は、複数のテンプレート定義の宣言として行動することができなかったことを発見しthis questionに関連しています

template<typename T> 
typename std::enable_if<GetType<T>::value < SVT_BASEOBJ,T>::type 
CastScriptVarConst(const ScriptVar_t& s) { 
    return (T) s; 
} 

template<typename T> 
typename std::enable_if<!(GetType<T>::value < SVT_BASEOBJ) 
         && std::is_base_of<CustomVar,T>::value,T>::type 
CastScriptVarConst(const ScriptVar_t& s) { 
    return *s.as<T>(); 
} 

そして、テンプレート定義と宣言の関係をよく理解したかったのです。

+0

本物の物語のための "C++ Templates:The Complete Guide"のコピーを手に入れてください。そして、はい、それは標準よりも消化可能です...実際はたくさんあります;) – 0xC0000022L

答えて

4

OK、のは、最初から始めましょう。テンプレートの「テンプレート名」は、テンプレート化される関数またはクラスの実際の名前です。つまり、

template<class T> T foo(T t); 

fooがテンプレート名です。関数テンプレートのために、彼らが同じであるかどうかを決定するための規則は、14.5.5.1「関数テンプレートのオーバーロード」で説明した、非常に長いです。そのセクション(言葉遣いと段落番号はC++ 11で変更されている場合がありますので、私は、ここではC++ 03から引用しています)のパラグラフ6は同等と機能的に同等の用語を定義し、テンプレートを含む式に適用されたときパラメーター。要するに

同等表現はおそらくテンプレートパラメータに異なる名前を持つから離れて同じであり、それらは同じものを評価するために起こる場合機能的に同等式は同じです。例えば、最初の二つf宣言は等価であるが、第三は、機能的に同等他の二つである: - それは全体の機能テンプレートにそれらの二つの定義を拡張するパラグラフ7に進む

template<int A, int B> 
void f(array<A + B>); 
template<int T1, int T2> 
void f(array<T1 + T2>); 
template<int A, int B> 
void f(array< mpl::plus< mpl::int<A>, mpl::int<B> >::value >); 

。同等の戻り値の型と引数の型を持つ場合、または機能的に同等の戻り値の型と引数の型しか持たない場合は、機能的に同等の2つの関数テンプレート(名前、スコープ、およびテンプレートのパラメータリスト)が一致します。あなたの第二の例を見てみると、この2つの関数は機能的に同等です: -

template<typename T> 
T foo(T t); 

template<typename T> 
typename identity<T>::type foo(T t); 

第7項は、プログラムは同等の機能的に同等ではなく、ある関数テンプレートの宣言が含まれている場合は、」悲惨な警告、プログラムを閉じ、診断が必要ない」と述べた。したがって、2番目の例は、有効ではないC++です。関数テンプレートの各宣言と定義を必要とするであろうそのような検出誤差は標準がそれを検出するための実装を必要としない理由である各パラメータと戻り型がどこから来たテンプレートの発現を、記述ASTとバイナリに注釈を付けることにします。 MSVCは、あなたが意図したどのようにあなたの第3の例をコンパイルするには正当化されるが、破ることは同じように正当化することでしょう。明示的なインスタンスに移る

、重要な部分は、14.7、「テンプレートのインスタンス化と専門化」です。パラグラフ5は、以下のすべてを許可しません。

  • テンプレートを複数回明示的にインスタンス化します。
  • 明示的にインスタンス化し、明示的に同じテンプレートを特化します。
  • 同じ引数セットのテンプレートを複数回明示的に特殊化しています。

また、「診断は必要ありません」というのは検出が非常に難しいためです。

だからあなたの明示的なインスタンスの例を拡張するために、次のコードは、二番目のルールを破って違法である: -

/* Template definition. */ 
template<typename T> 
T foo(T t) 
{ ... } 

/* Specialization, OK in itself. */ 
template< > 
int foo(int t) 
{ ... } 

/* Explicit instantiation, OK in itself. */ 
template< > 
int foo(int t); 

これは関係なく、明示的な特殊の場所と明示的なインスタンスのが、もちろん違法です診断が必要ないため、一部のコンパイラで有用な結果が得られる可能性があります。明示的なインスタンス化と明示的な特殊化の違いにも注意してください。この例では、よく形成されて

template<typename T> 
T f(T f) 
{ ... } 

template< > 
int f(int); 

void g(void) 
{ f(3); } 

が、それは明示的なインスタンスを持っているので、: - - :それはそれを定義せずに、明示的な特殊化を宣言するため、次の例では、病気に形成されて

template<typename T> 
T f(T f) 
{ ... } 

template f(int); 

void g(void) 
{ f(3); } 

< >すべての違いを生み出す。明示的な特殊化を定義する場合でも、を使用する前ににする必要があります。それ以外の場合、コンパイラはすでにそのテンプレートの暗黙のインスタンスを生成している可能性があります。これは14.7.3「明示的な専門化」第6段落にあります。読んでいた場所のすぐ下にあります。診断は必要ありません。同じ例を適応させるために、これは悪い形成されている: -

template<typename T> 
T f(T f) 
{ ... } 

void g(void) 
{ f(3); } // Implicitly specializes int f(int) 

template< > 
int f(int) // Too late for an explicit specialization 
{ ... } 

あなたはまだ十分に混乱していなかった場合は、あなたの最後の例を見てみ: -

template<typename T> 
T foo(T t) { ... } 

template<typename T> 
int foo(int t) { ... } 

fooの2番目の定義をではない最初の定義の特化。具体的にはtemplate<typename T> T foo(T)となるのはtemplate< > int foo(int)である必要があります。しかし、それは問題ありません。関数のオーバーロードは許可されており、関数テンプレートと通常の関数の間で許可されています。 foo(3)という形式の呼び出しは、最初の定義を使用します。そのテンプレートパラメータTは引数の型から導き出すことができるからです。 2番目の定義では、引数型からテンプレートパラメータを導出できません。コールは最初の定義と曖昧でない場合にのみ、明示的にTを指定することで、あなただけの2番目の定義に到達し、することができます -

f<int>(3); // ambiguous 
f<string>(3); // can only be the second one 

関数テンプレートのオーバーロードの解決を行うための全体のプロセスは、ここで説明するには長すぎます。興味のある方は14.8.3を読んで、さらに質問してください:

関連する問題