ADLが関数テンプレートを見つけられないのはなぜですか?


83

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.
}

ns :: frob()を記述せずにfrob()を動作させることを期待しているということですか?
サイモン

はい、非テンプレート関数の方法で。
ヒュー

参考までに、上記のコードはComeauでも失敗します:comeaucomputing.com/tryitout-追加するusing namespace ns;か、ns::修飾がコンパイルに合格します。これは良い質問です。
fbrereto 2010年

3
@Huw:それに噛まれただけです:)明示的な資格がADLをどのように除外するかおかしいと思います:/
Matthieu M.

1
@マット:ハハ、そして私も今。小さなプログラミングの世界。
GManNickG 2011年

回答:


86

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

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
}

9
これの理由は何ですか?奇妙な要件のようです。つまり、構文形式は何かと何の関係があるのでしょうか。
軌道上でのライトネスレース

22
Vandevoorde&Josuttisの@LightnessRacesinOrbitセクション9.3.5は、これ構文上の問題ある理由を説明しています(OPの例に採用された名前付け):「コンパイラは、テンプレート引数リストでf<3>(b)ある<3>と判断するまで、それが関数呼び出し引数であると判断できません。逆に、<3>テンプレートであることが判明f()するまで、それがテンプレート引数リストであると判断することはできません。この鶏が先か卵が先かという問題は解決できないため、式はとして解析されますが(f<3)>(b)、これは意味がありません。」これはtemplate、メンバー関数テンプレートの明確化構文に似ていることに注意してください。
templateRex

9
この問題を修正する提案はありますか? template f<3>(b)より良い構文かもしれませんか?
balki 2015年

1
@AngelusMortis式の ( 式... )(括弧の前に演算子がないことに注意してください)は常に関数呼び出しであり、コンパイラは開いた括弧を見るとすぐにそれを認識します。ここでの問題は<、演算子とテンプレート引数リストの開始の両方として機能する可能性があることです。コンパイラは、どちらを特定するために多くの追加の解析を行う必要があります(これが不可能なコードの配置がある可能性があります)明確に行う)。おそらくコンパイラ開発者の髪を救うために、それを違法にすることを選んだ標準的な作者のようです。
ミラル2017年

2
この要件はC ++ 20で解除され、OPのコードは整形式になりました:)
Rakete1111 2018

8

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

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

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

live demo


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);
}

0

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


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

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

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

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*/ }
}

1
いいえ、名前空間を取得しても、まだ問題がありますね。使用後の特殊化はC ++の通常の問題であり、使用後に宣言された場合、特殊化された形式は使用されません。
Kornel Kisielewicz 2010年

@Kornel:ああ、それは別のエラーを出します。もう1つは私が説明したことと一致しています。それを指摘してくれてありがとう。
トルバドゥール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.