2011-03-01 33 views
5

テンプレートメソッドを定義するときに戻り値型にテンプレート引数が必要だが、引数型ではない理由が分かっていますか?例:メソッド定義中の戻り型でテンプレート引数が必要

template<typename T> 
struct Car { 
    Car drive(Car); // will be defined after the template declaration. 
}; 

// Attempt #1: does not compile. 
// Error: use of class template Car requires template arguments 
template<typename T> 
inline Car Car<T>::drive(Car) {} 

// Attempt #2: compiles! 
// The only difference is the use of template argument in return type. 
// However, note that the argument to func does not require template argument! 
template<typename T> 
inline Car<T> Car<T>::drive(Car) {} 

テンプレート引数は、戻り値型のために必要なものの、引数型のために必要とされていない理由はわかりません。試み#1が失敗したとき、私はあまりにも失敗する試み#2を期待していたと私は必要があるだろうと予想:

template<typename T> 
inline Car<T> Car<T>::drive(Car<T>) {} // but no need to go this far. 

が、2位に働いを試み!

この現象が発生する理由はありますか?

+1

これは、左から右に解析するためだと思います。 'template typename T>の後、テンプレートタイプがまだわかりません。インラインカーではまだ何らかの関数を宣言している可能性があるので、 'Car'は有効ではないので' Car 'を明確にする必要があります。 'Car :: ...'の後、あなたは 'struct car 'のコンテキスト内にあり、引数 'Car'は暗黙的に' Car 'です。 –

答えて

2

これは、メソッドの引数の型がクラスのスコープを使用して導出されるためです。戻り値の型は、クラススコープの外部に定義されている場合、クラスが定義されているのと同じスコープから導き出されます。これは、テンプレートだけでなく、すべてに当てはまります。

class Foo 
{ 
    typedef int Bar; 

    Bar foo() const; 
}; 

Bar 
Foo::foo() const 
{ 
    return 0; 
} 

を...そしてあなたがfooの範囲からそのバーがあり、正確に伝える必要があり、それを修正するために:あなたの例に追加するには、次のようにコンパイルされません

Foo::Bar 
Foo::foo() const 
{ 
    return 0; 
} 
+0

私にルールを知らせてくれてありがとう。私は戻り型が引数型とは異なる範囲で扱われていると思っていましたが、その背後にある根拠を理解できません。パーサが引数型のスコープ内でリターン型を扱うのはなぜですか? – kirakun

9

まず、あなたはこれが意味をなさないことを認めます:Car c;、右? Carにはテンプレート引数が必要です。そのため、戻り値の型とクラス名で指定する必要があります。

しかし、スコープ解決演算子(::)後、Car<T>あること* Carとして注射のでCarCar<T>に別名です。しかし、これはCar<T>の範囲内でのみ起こります。なぜなら、それ以外の場所はどこでも必要ですが、::の後には必要ではありません。もちろん、引数を明示的に指定することは自由です。

template <typename T> 
struct foo 
{ 
    // as if the compiler did this: 
    typedef foo<T> foo; // (of course, actually illegal) 
}; 

foofoo<T>としてfoo<T>の範囲内で提供されています:


*この機能は優れているが、このように説明しました。しかし、scope-resolution演算子の後では、そのスコープが使用可能であり、テンプレート引数はオプションです。

+0

私はあなたが独立してコメントしたことをあなたが独立して言ったので、私はあなたに投票したいと思います。しかし、私はこれが正しい答えであると確信していたならば、 ;-) –

0

をN3225から、3.4 .1/8(未修飾の名前参照)では、メンバー関数のdeclarator-idの後に使用される名前は、クラス定義内で最初に検索されます。

関数の宣言-ID次のクラスX のメンバー関数(9.3)の定義に使用される名前 (すなわち、タイプまたはデフォルト引数で、例えば、発生する非修飾名であります 節または関数本体の式)または Xの 非静的データメンバー(9.2)の ブレースまたは同等イニシャライザXは、次のいずれかの のいずれかで宣言されます方法:

- クラスXでの使用前、または Xの基本クラスのメンバ(10。2)、または

- Xは YにおけるXの定義の前に、クラスY (9.7)のネストされたクラスであり、又はYの塩基 クラスのメンバーでなければならない場合(この参照は に適用、Yの封入クラスに向ける は、クラスを囲む最も内側から始まる)、または

- Xは、ローカルクラス (9.8)であるか、またはクラスの定義の前に、ローカル クラスのネストされたクラスである場合 クラスXの定義 を囲むブロック内のX、または

- Xは 名前空間Nのメンバーであるか、またはNのメンバーである クラスのネストされたクラスである、又は ローカルクラス又はある関数の ローカルクラス内のネストされたクラスである場合ネームスペースNの クラスXの定義の前、またはN のネームスペースの1つのうちのNの メンバー。

次に、inject-class-nameは名前検索を目的とした通常のクラスメンバで、inject-class-nameは他のメンバの前にクラスの先頭で宣言されます。

3.3.2/7

注入クラス名(条項9)の宣言のポイントは、クラス定義の開口 ブレース直後 あります。

3.4/3

クラス (条項9)の注入されたクラス名は、名前の隠蔽とルックアップの目的のために そのクラスのメンバーであると考えられます。

最後に、テンプレートの、

14.6.1/3

専門は またはなしのいずれかを使用することができるクラス テンプレートまたはクラステンプレートの注入されたクラス名テンプレートの引数リスト がスコープ内にある場合はどこにでも置くことができます。

それは通常の名前として検索され、グローバルクラスの車を見つけるためしたがって、

template<typename T> 
inline Car<T> Car<T>::drive(Car) {} 

最初の戻り値の型の名前の車は、注入されたクラス名を見つけることができません。

しかし、2番目のパラメータ型Carは、関数のdelcarator-idの後にあるため、注入されたクラス名を見つけることができます。注入クラス名は、ブラケットTブラケットの有無にかかわらず使用できます。

あなたは今答えを得ました。

+0

しかしこれはC++構文のレイアウトの問題のようであり、意味論ではないようです。インライン・カー :: drive(x Car)Car {} 'のように、' 'Go''言語のように、C++が引数の後にリターン・タイプを宣言したとしたら、両方のパラメータ・タイプが宣言子-id 。 – kirakun

+0

はい、そうです。 C++ 0xの場合、 のように書く 'auto Car :: drive(Car) - > Car {}'末尾の戻り値の型が関数のdeclarator-idの後にあるため、 ''は必要ありません。こうして、注入されたクラス名 – user534498

関連する問題