3

次のような関数があるとします。

template<typename T> inline
typename std::enable_if<has_member_foo<T>::value,int>::type
foo( T const &t ) {
  return t.foo();
}

template<typename T> inline
typename std::enable_if<!has_member_foo<T>::value,int>::type
foo( T const& ) {
  return 0;
}

template<typename T> inline
int call_foo( T const &t ) {
  return sizeof( T ) + foo( t );
}

これはほとんど問題なく動作しますが、後で特定の型のオーバーロードを追加すると、次のようになります。

inline int foo( std::string const &s ) {
  return s.size();
}

の定義のに追加するとcall_foo()、オーバーロードは によって使用されませんcall_foo()。ただし、オーバーロード コードを の定義の前に移動するとcall_foo()、それが使用されます。

最初のケースでオーバーロードが使用されないのはなぜですか? がコードの他の場所で使用される時点call_foo()でインスタンス化されるまでに、コンパイラは既にオーバーロードを認識しているのに、なぜそれを使用しないのでしょうか?

私の元のコードには、同様に を使用して保護されfoo()たテンプレート化されたクラスの静的メンバー関数として関数が含まれていたことに注意してください。そのコード、つまりテンプレート クラスの特殊化は、使用に提供された場合でも機能しました。foo_traitsenable_if call_foo()

問題があれば、私はg++Mac OS X 10.7.4 で 4.6 を使用しています。

4

1 に答える 1

3

標準 (とにかく C++98) を 14.6.4.2/1 にすると、次のようになります。

テンプレート パラメーターに依存する関数呼び出しの場合、関数名が非修飾 ID であるがテンプレート ID ではない場合、候補関数は通常のルックアップ ルール (3.4.1、3.4.2) を使用して検索されますが、次の点が異なります。

— 非修飾名ルックアップ (3.4.1) を使用したルックアップの部分では、テンプレート定義コンテキストからの外部リンケージを持つ関数宣言のみが検出されます。

この場合、修飾されたテンプレート名をtemplate-id意味します。<template-params>これは、テンプレート定義のコンテキストで表示される関数のみが考慮されるという、プログラムで観察したことを正確に示しています。考えてみると、そうでないと、テンプレートの意味をそれに続くものに基づいてばかげて簡単に変更し、1 つの定義ルールに違反することになります。

于 2012-07-12T15:06:07.917 に答える