テンプレートのコンテキストでの関数名の検索について混乱しています。テンプレートがインスタンス化されるまで、コンパイラはテンプレート化されたコードで引数に依存する識別子の検索を遅らせることを知っています。つまり、テンプレート化されたコード内で構文エラーが発生したり、存在しない関数を呼び出したりすることがありますが、テンプレートを実際にインスタンス化しない限り、コンパイラは文句を言いません。
ただし、さまざまなコンパイラ間に不一致があることがわかりました。標準自体が何を必要としているのかを知りたいと思っています。
次のコードを検討してください。
#include <iostream>
class Foo
{
public:
template <class T>
void bar(T v)
{
do_something(v);
}
};
void do_something(std::string s)
{
std::cout << "do_something(std::string)" << std::endl;
}
void do_something(int x)
{
std::cout << "do_something(int)" << std::endl;
}
int main()
{
Foo f;
f.bar("abc");
f.bar(123);
}
テンプレート メンバー関数は、まだ宣言されていないという引数に依存しないグローバル関数をFoo::bar
呼び出すことに注意してください。do_something
それでも、GCC 4.6.3 は上記のプログラムを問題なくコンパイルします。実行すると、出力は次のようになります。
do_something(std::string)
do_something(int)
そのため、コンパイラはテンプレートがインスタンス化されるまで識別子の検索を遅らせたように見え、その時点でdo_something
.
対照的に、GCC 4.7.2 は上記のプログラムをコンパイルしません。次のエラーが発生します。
test.cc: In instantiation of ‘void Foo::bar(T) [with T = const char*]’:
test.cc:27:13: required from here
test.cc:10:3: error: ‘do_something’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
test.cc:19:6: note: ‘void do_something(int)’ declared here, later in the translation unit
したがって、GCC 4.7.2 は が後で宣言されていることを認識していますが、引数に依存しない do_something
ため、プログラムのコンパイルを拒否します。do_something
したがって、ここではおそらく GCC 4.7.2 が正しく、GCC 4.6.3 は正しくないと思います。おそらく、が定義されるdo_something
前に宣言する必要があります。これに関する問題は、自分のクラスのユーザーがの独自のオーバーロードを実装しての動作を拡張Foo::bar
できるようにしたいと考えていることです。次のように書く必要があります。Foo
Foo::bar
do_something
#include <iostream>
template <class T>
void do_something(T v)
{
std::cout << "do_something(T)" << std::endl;
}
class Foo
{
public:
template <class T>
void bar(T v)
{
do_something(v);
}
};
void do_something(int x)
{
std::cout << "do_something(int)" << std::endl;
}
int main()
{
Foo f;
f.bar("abc");
f.bar(123);
}
ここでの問題は、 のオーバーロードがdo_something
内から見えないFoo::bar
ため、呼び出されないことです。したがって、 を呼び出しても、 のオーバーロードではなくdo_something(int)
呼び出します。したがって、GCC 4.6.3 と GCC 4.7.2 の両方で、上記のプログラムは次のように出力します。do_something(T)
int
do_something(T)
do_something(T)
それで、ここでいくつかの解決策は何ですか?Foo::bar
ユーザーが独自のオーバーロードを実装して拡張できるようにするにはどうすればよいdo_something
ですか?