なぜADLは関数テンプレートを見つけられないのですか?
-
22-10-2019 - |
質問
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.
}
解決
この部分はそれを説明しています:
C ++標準03 14.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
}
他のヒント
わずかに受け入れられた答えを改良したいと思います。 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);
}
C ++ 20以降、ADLは明示的な関数テンプレートでも正常に動作します。これが提案です:P0846R0:表示されていないADLおよび関数テンプレート:
ユーザーにテンプレートキーワードを使用するように要求する代わりに、通常のルックアップが結果を生成しないか、1つ以上の関数を見つけ、AA "<"が続く名前のいずれかの名前が次のように扱われるように、ルックアップルールの改訂が提案されました。関数テンプレート名が見つかった場合、ADLが実行されます。
現在、GCC 9のみがこの機能を実装しているため、例をコンパイルできます。
編集:いいえ、これは正しくありません。見る @Kornelの答え.
確かではありませんが、Stroustrupの「C ++プログラミング言語」に相談したことがあります。 そうかもしれない 原因になります。
以来 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*/ }
}