2012-08-08 29 views
5

C++よくある質問35.16フレンドメンバーの機能が機能テンプレートとして自動的に認識されないのはなぜですか?

http://www.parashift.com/c++-faq-lite/template-friends.html

#include <iostream> 

template<typename T> 
class Foo { 
public: 
    Foo(T const& value = T()); 
    friend Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs); 
    friend std::ostream& operator<< (std::ostream& o, const Foo<T>& x); 
private: 
    T value_; 
}; 

autorの主張:

「コンパイラが適切なクラス定義の仕方まで友人ラインを見たときに思わぬ障害が発生し

。現時点では、友人の機能自体がテンプレート であることはまだわかりません(テンプレートテンプレートのメンバー関数はデフォルトで関数テンプレートになっていません);それは彼らがこのような非テンプレートです前提としています」

Foo<int> operator+ (const Foo<int>& lhs, const Foo<int>& rhs) 
{ ... } 

std::ostream& operator<< (std::ostream& o, const Foo<int>& x) 
{ ... } 

上記以外のテンプレートですなぜ? intを介してインスタンス化されるこれらのテンプレートではありませんか?

「あなたは演算子+またはオペレータ< <関数を呼び出すときに、この仮定は非テンプレート関数への呼び出しを生成するようにコンパイラーになりますが、あなたが実際に定義されたことがないので、リンカがあなたに 『未定義の外部』エラーになりますこれらのテンプレート以外の関数「

は、実際には、コンパイラは関数テンプレート上記のように認識させるために、プログラマは以下のように明示的にこれを行うことがあります。

template<typename T> class Foo; // pre-declare the template class itself 
template<typename T> Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs); 
template<typename T> std::ostream& operator<< (std::ostream& o, const Foo<T>& x); 

誰が説明してもらえますか?私はこれが非常に厄介で、コンパイラがTを 'int'に置き換えてClass Fooのインスタンスをインスタンス化するのではなく、それを1日と呼びます。

ありがとうございました。

答えて

5

クラステンプレートメンバ関数はテンプレートの一部であるため、テンプレートでインスタンス化されますが、フレンズは使用できません。非テンプレートケースを考えてみましょう:

struct S { 
    friend void foo(S); 
}; 

void foo(S)は、この時点で宣言する必要がないことに注意してください。 friend宣言は、void foo(S)がと定義されている場合は、Sにアクセスし、となります。実際に定義されることは決してないかもしれません。テンプレートと

、状況は同じです:

template<typename T> struct S { 
    friend void foo(S); 
}; 

これは、関数void foo(S<T>)定義されている場合は任意のタイプTのためにその機能がS<T>へのアクセス権を持っていると言っています。この関数はオーバーロードすることで、具体的な機能であることが予想されます。

void foo(S<char>) { } 
void foo(S<int>) { } 

コンパイラはあなたがすべてのTのために使用することができる関数テンプレートを供給するために、後に計画していることを知りません。代わりに、適切な関数テンプレートがすでに宣言されている場合は、角括弧を追加することを指定すると、インスタンス化されます。

なぜ、テンプレートを前方宣言しなければならないかについては、「テンプレート」に宣言が1つだけある必要はありません。考えてみましょう:

#include <iostream> 
template<typename T> struct S; 
template<typename T> void foo(S<T>); 
template<typename T> void foo(S<T *>); 
template<typename T> struct S { 
    friend void foo<>(S); 
}; 
template<typename T> void foo(S<T>) { std::cout << "template template friend\n"; } 
template<typename T> void foo(S<T *>) { std::cout << "template specialization template friend\n"; } 
template void foo(S<void *>); 
int main() { 
    foo(S<int>()); 
    foo(S<void *>()); 
} 

はここfooの2つの専門がある、と彼らはfriendがそれらの間で選択できるように、両方が前方に宣言する必要があります。

+0

私はいくつかを理解しますが、すべてではありません。 '友達Foo 演算子+(const Foo &lhs、const Foo & rhs);'に沿って、コンパイラは何を表示しますか? 演算子+はFooのフレンド関数になります。演算子+の宣言が見つかりましたか? – user1559625

+0

@ user1559625コンパイラは、 'Foo 演算子+(const Foo &lhs、const Foo & rhs);')が定義されていればそれを友人に見せます。 – ecatmur

+0

修正:インスタンス化のためのテンプレートを指定する構文が異なります(余分な山括弧)。 – ecatmur

関連する問題