7

このコードを考えてみましょう。

#include <iostream>
#include <vector>

template<typename A>
void foo(A& a) {
    std::cout << "the wrong foo" << std::endl;
}

template<typename A>
void do_stuff(A& a) {
    foo(a);
}

template<typename X>
void foo(std::vector<X>& a) {
    std::cout << "the right foo" << std::endl;
}

int main()
{
    std::vector<int> q;
    do_stuff(q);
}

なぜ「間違った」fooと呼ばれるのですか?fooの最初の宣言が削除されると、正しいfooが呼び出されます。

gcc4.6.3を使用しています。

更新: 関数が次の順序で宣言されている場合、正しいfooが呼び出されます。

template<typename A> void do_stuff(A& a) { ... }
template<typename A> void foo(A& a) { ... }
template<typename X> void foo(std::vector<X>& a) { ... }
4

3 に答える 3

4

観察された動作は正しく、次のようfoo(a)なタイプ依存の式も正しいです。

14.6.2.2 Type-dependent expressions                         [temp.dep.expr]

1) Except as described below, an expression is type-dependent if any
   subexpression is type-dependent.

2) this is type-dependent if the class type of the enclosing member
   function is dependent (14.6.2.1).

3) An id-expression is type-dependent if it contains

    — an identifier associated by name lookup with one or more declarations 
      declared with a dependent type,
    ...

および14.6.4(扶養家族の名前のresoultion)の下で:

14.6.4.2 Candidate functions                              [temp.dep.candidate]

For a function call that depends on a template parameter, the candidate
functions are found using the usual lookup rules (3.4.1, 3.4.2, 3.4.3) except
that:

— For the part of the lookup using unqualified name lookup (3.4.1) or qualified
  name lookup (3.4.3), only function declarations from the template definition 
  context are found.
— For the part of the lookup using associated namespaces (3.4.2), only function
  declarations found in either the template definition context or the template
  instantiation context are found.

If the function name is an unqualified-id and the call would be ill-formed or
would find a better match had the lookup within the associated namespaces
considered all the function declarations with external linkage introduced in
those namespaces in all translation units, not just considering those
declarations found in the template definition and template instantiation
contexts, then the program has undefined behavior.

テンプレート定義の時点でfoo()表示されるのは「間違った」だけであるため、「間違った」が選択されます。また、関数の引数の型に関連付けられた名前空間にないため、「正しい」は考慮されません。foo()

「正しい」foo()が関連付けられた名前空間に含まれるようにコードを変更すると、「間違った」の代わりにコードが選択されますfoo()。(この特定のケースでは、標準では許可されていないため、以下を実行しないでください。ただし、独自の名前空間/タイプを使用すると、これが機能するはずです)

#include <iostream>
#include <vector>

template<typename A> void foo(A& a)
{
    std::cout << "the wrong foo" << std::endl;
}

template<typename A>
void do_stuff(A& a) {
    foo(a);
}

namespace std { // evil, don't do this with namespace std!

template<typename X>
void foo(std::vector<X>& a) {
    std::cout << "the right foo" << std::endl;
}

}

int main()
{
    std::vector<int> q;
    do_stuff(q); // calls the "right" foo()
}
于 2012-09-21T13:06:28.233 に答える
3

テンプレート定義内で、依存fooしない名前(つまり、テンプレートパラメーターに依存しない名前)の名前検索は、インスタンス化された場所ではなく、テンプレートが定義された場所で実行されます。これは、標準で指定されています。

C ++ 11 14.6.3テンプレート定義で使用される非依存名は、通常の名前ルックアップを使用して検出され、使用されるポイントでバインドされます。

あなたと同じような例で説明します:

void g(double);
void h();

template<class T> class Z {
public:
  void f() {
    g(1);           // calls g(double)
    h++;            // ill-formed: cannot increment function;
                    // this could be diagnosed either here or
                    // at the point of instantiation
  }
};

void g(int);        // not in scope at the point of the template
                    // definition, not considered for the call g(1)

更新について:両方のfoo宣言が後に配置されdo_stuffているため、プログラムの形式が正しくないはずであり、GCCは、インスタンス化のポイントが使用時に失敗するまでルックアップを(明らかに)延期するのは正しくないと思います。

更新:コメントに記載されているように、この動作は確かに正しくなく、gcc-4.7で修正されました。

于 2012-09-21T13:19:27.893 に答える
2

あなたが呼ぶときdo_stuffのタイプはaですstd::vector<int>。次に、コンパイラーは、を取得して使用できること('wrong' )fooを検索し、まだ宣言されていないことを検出します。この宣言を削除すると、テンプレートを使用する前にテンプレートを定義する必要があるため、コンパイラエラーが発生するはずです。そうでない場合は、言語の機能ではなく、gccのテンプレートシステムのバグが原因である可能性があります。で試してみると、次のように表示されます。std::vector<int>foo(A&)foofoo(std::vector<B>&)clang++

test.cpp:6:5: error: call to function 'foo' that is neither visible in the template definition nor found by
  argument-dependent lookup
foo(a);
^

編集:これは実際にはテンプレートの処理方法のエラーのようです。テンプレートのインスタンス化の前に表示されるclang++2番目の呼び出しを解決できるはずです。foo

于 2012-09-21T12:14:41.253 に答える