次のコードを検討してください。
#include <iostream>
void f(int) { }
void f(int, short) { }
template<typename... Ts> void g(void (*)(Ts...))
{
std::cout << sizeof...(Ts) << '\n';
}
template<typename T, typename... Ts> void h(void (*)(T, Ts...))
{
std::cout << sizeof...(Ts) << '\n';
}
int main()
{
g(f); // #1
g<int>(f); // #2
h(f); // #3
h<int>(f); // #4
}
意図は、本体の各行をmain()
個別に試すことです。私の予想では、4 つの呼び出しはすべてあいまいであり、コンパイラ エラーが発生するだろうということでした。
私はコードをテストしました:
- Clang 3.6.0 と GCC 4.9.2 の両方で
-Wall -Wextra -pedantic -std=c++14
(-std=c++1y
for GCC) を使用 - エラー メッセージの文言のわずかな違いを除いて、これらすべてのケースで同じ動作。 - Visual C++ 2013 Update 4 と Visual C++ 2015 CTP6 - これも同じ動作なので、今後は "MSVC" と呼びます。
Clang と GCC:
#1
: コンパイラ エラーです。紛らわしいメッセージが表示されます。基本的にはno overload of 'f' matching 'void (*)()'
. 何?no-param 宣言はどこから来たのですか?#3
: コンパイラ エラー、別の紛らわしいメッセージ:couldn't infer template argument 'T'
。そこで失敗する可能性のあるすべてのことの中で、の引数を推測することはT
、私が期待する最後のものになるでしょう...#2
and#4
: エラーも警告もなくコンパイルされ、最初のオーバーロードが選択されます。
4 つのケースすべてで、オーバーロードの 1 つ (いずれか 1 つ) を削除すると、コードは正常にコンパイルされ、残りの関数が選択されます。これは、Clang と GCC の不一致のように見えます: 結局のところ、両方のオーバーロードで別々に推定が成功した場合、ケース#2
と#4
? 両者は完全に一致していませんか?
さて、MSVC:
#1
、#3
および#4
: コンパイラ エラー、適切なメッセージ:cannot deduce template argument as function argument is ambiguous
。今、それが私が話していることです!ちょっと待って...#2
: エラーも警告もなくコンパイルされ、最初のオーバーロードが選択されます。2 つのオーバーロードを別々に試してみると、最初の 1 つだけが一致します。2 番目のものはエラーを生成します:cannot convert argument 1 from 'void (*)(int,short)' to 'void (*)(int)'
. もうあまり良くありません。
case で何を探しているかを明確にするために#2
、これは標準 (N4296、C++14 final 後の最初のドラフト) が [14.8.1p9] で述べていることです。
テンプレート引数推定は、明示的に指定されたテンプレート引数がシーケンスに含まれている場合でも、テンプレート パラメーター パックに対応するテンプレート引数のシーケンスを拡張できます。
この部分は MSVC ではうまく機能しないようで、 の最初のオーバーロードを選択します#2
。
これまでのところ、MSVC は完全に正しいとは言えませんが、少なくとも比較的一貫しているようです。Clang と GCC はどうなっていますか? それぞれの場合の標準に従った正しい動作は何ですか?