関数型の引数のテンプレート引数の控除


10

次のプログラムを検討してください。

#include <iostream>

template <typename T>
void f( void ( *fn )( T ) )
{
    fn( 42 );
}

void g( int x )
{
    std::cout << "g( " << x << " );\n";
}

int main()
{
    f( g );
}

プログラムは正常にコンパイルされ、その出力は

g( 42 );

今度は、非テンプレート関数の名前を変更してみましょうgf

#include <iostream>

template <typename T>
void f( void ( *fn )( T ) )
{
    fn( 42 );
}

void f( int x )
{
    std::cout << "f( " << x << " );\n"; 
}

int main()
{
    f( f );
}

現在、プログラムはgcc HEAD 10.0.0 20200およびclang HEAD 10.0.0ではコンパイルされませんが、Visual C ++ 2019では正常にコンパイルされます。

たとえば、コンパイラgccは次のメッセージセットを発行します。

prog.cc: In function 'int main()':
prog.cc:22:10: error: no matching function for call to 'f(<unresolved overloaded function type>)'
   22 |     f( f );
      |          ^
prog.cc:4:6: note: candidate: 'template<class T> void f(void (*)(T))'
    4 | void f( void ( *fn )( T ) )
      |      ^
prog.cc:4:6: note:   template argument deduction/substitution failed:
prog.cc:22:10: note:   couldn't deduce template parameter 'T'
   22 |     f( f );
      |          ^
prog.cc:14:6: note: candidate: 'void f(int)'
   14 | void f( int x )
      |      ^
prog.cc:14:13: note:   no known conversion for argument 1 from '<unresolved overloaded function type>' to 'int'
   14 | void f( int x )
      |         ~~~~^

したがって、問題が発生します。コードをコンパイルする必要がありますか。また、コードがgccおよびclangによってコンパイルされない理由は何ですか。



注:最初の例では、g(の代わりに&g)関数テンプレートに渡すと、型の減衰が発生します(関数の左辺値参照は、関数へのポインターに減衰します:void(&)(T)=> void(*)(T))。この暗黙の変換はf、より適切な一致を持つ他のオーバーロードがないために発生します。2番目の例ffは、どちらが引数なのかわからないため、実際に呼び出したい曖昧さがあります。
Xeverous

回答:


7

gccとclangは正しいように思えます。これはコンパイルしないでください。T推論したい関数パラメーターは、ここで提供される引数が関数テンプレート[temp.deduct.type] /5.5を含むオーバーロードセットである場合、非推論コンテキストになります。

推定されないコンテキストは次のとおりです。

  • […]
  • 関連する関数の引数が関数またはオーバーロードされた関数のセット([over.over])であり、次の1つ以上が適用されるため、引数の推定ができない関数パラメーター。

    • […]
    • 引数として提供される関数のセットには、1つ以上の関数テンプレートが含まれています。
  • […]

したがって、T推定できないため、変換がないため、他のオーバーロードは実行できません。まさにgccが言うこと…


0

これらは2つのオーバーロードされた関数であり、非テンプレート関数はテンプレート関数と比較して選択する必要があるため、f(int x)が選択されたため、intを渡さなければならない関数に引数として関数を渡すことはできません。以下が動作するはずです。ありがとう

void f( void ( *fn )( int ) ){
  fn( 42 );
}
void f( int x ){
    std::cout << "f( " << x << " );\n";
  }

  int main(){

     f( f );
  }

非テンプレート関数は、他の方法でオーバーロード解決中にタイがある場合にのみ推奨されます。質問のコードはその点に達しvoid f<int>(void(int))ていません。どちらのレベルでオーバーロード解決を行うために生成された特殊化はありません。
デイビスヘリング
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.