86

C ++仕様のどの部分が、引数に依存するルックアップが、関連付けられた名前空間のセットで関数テンプレートを見つけることを制限していますか?言い換えると、main以下の最後の呼び出しがコンパイルに失敗するのはなぜですか?

namespace ns {
    struct foo {};
    template<int i> void frob(foo const&) {}
    void non_template(foo const&) {}
}

int main() {
    ns::foo f;
    non_template(f); // This is fine.
    frob<0>(f); // This is not.
}
4

4 に答える 4

88

この部分はそれを説明します:

C++標準0314.8.1.6

[注:単純な関数名の場合、関数名が呼び出しのスコープ内に表示されていない場合でも、引数依存のルックアップ(3.4.2)が適用されます。これは、呼び出しがまだ関数呼び出し(3.4.1)の構文形式を持っているためです。ただし、明示的なテンプレート引数を持つ関数テンプレートが使用されている場合、呼び出しのポイントにその名前の関数テンプレートが表示されていない限り、呼び出しには正しい構文形式がありません。そのような名前が表示されない場合、呼び出しは構文的に整形式ではなく、引数に依存するルックアップは適用されません。そのような名前が表示されている場合は、引数依存のルックアップが適用され、他の名前空間に追加の関数テンプレートが見つかる場合があります。

namespace A {
  struct B { };
  template<int X> void f(B);
}
namespace C {
  template<class T> void f(T t);
}
void g(A::B b) {
  f<3>(b);    //ill-formed: not a function call
  A::f<3>(b); //well-formed
  C::f<3>(b); //ill-formed; argument dependent lookup
              // applies only to unqualified names
  using C::f;
  f<3>(b);    //well-formed because C::f is visible; then
              // A::f is found by argument dependent lookup
}
于 2010-06-01T22:18:24.253 に答える
11

c ++ 20以降、adlは明示的な関数テンプレートでも正常に機能します。提案は次の とおりです。P0846R0:表示されないADLおよび関数テンプレート

ユーザーにtemplateキーワードの使用を要求する代わりに、ルックアップルールの改訂が提案され、通常のルックアップで結果が生成されないか、1つ以上の関数が検出され、その後にaa"<"が続く名前は次のように扱われます。関数テンプレート名が見つかり、ADLが実行される場合。

現在、GCC 9のみがこの機能を実装しているため、サンプルをコンパイルできます。

live demo

于 2018-12-30T02:37:44.557 に答える
5

少し受け入れられた答えを洗練させたいと思います。OPの質問では明確ではありませんが、標準(Kornelが引用)の重要な部分は次のとおりです(私の強調):

ただし、明示的なテンプレート引数を持つ関数テンプレートが使用されている場合、呼び出しには正しい構文形式がありません

したがって、禁止されているのは、ADLに依存し、明示的なテンプレート引数を使用することです。残念ながら、型以外のテンプレート引数を使用するには、明示的な引数を使用する必要があります(デフォルト値がない場合)。

以下はこれを示すサンプルコードです。

[住む]

#include <string>
#include <utility>

namespace C {
  struct B { };
  template<class T> void f(T t){}
}

void g(C::B b) {
  f(b);           // OK
  //f<C::B>(b);   // ill-formed: not a function call, but only 
                  //  because explicit template argument were used

  std::string s;
  move(s);                      // OK
  //move<std::string&>(s);      // Error, again because 
                                //  explicit template argument were used
  std::move<std::string&>(s);   // Ok
}

int main()
{
 C::B b;
 g(b);
}
于 2016-08-30T09:05:52.177 に答える
0

編集:いいえ、これは正しくありません。@Kornelの回答を参照してください。


完全にはわかりませんが、Stroustrupの「C ++プログラミング言語」を参考にしたことがありますが、付録Cのセクション13.8.4原因である可能性があります。

はテンプレートなので、呼び出した後のある時点でfrobそれを特殊化できると考えられます。これは、実装には、インスタンス化の時点または変換ユニットの処理の最後に選択できるように見えるときに、どちらを呼び出すi=0かを選択する2つの可能な方法が残されることを意味します。frob

だから、問題はあなたができることだと思います

namespace ns {
    struct foo {};
    template<int i> void frob(foo const&) {}
}

int main() {
    ns::foo f;
    frob<0>(f);
    return 0;
}

namespace ns {
    template<> void frob< 0 >(foo const&) { /* Do something different*/ }
}
于 2010-06-01T22:40:37.193 に答える