Pengurangan argumen template untuk argumen tipe fungsi

10

Pertimbangkan program berikut.

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

Program berhasil dikompilasi dan hasilnya adalah

g( 42 );

Sekarang mari kita ganti nama fungsi non-templat gmenjadi f.

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

Sekarang program tidak dikompilasi oleh gcc HEAD 10.0.0 20200 dan clang HEAD 10.0.0 tetapi berhasil dikompilasi oleh Visual C ++ 2019 ..

Sebagai contoh, compiler gcc mengeluarkan set pesan berikut.

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 )
      |         ~~~~^

Maka muncul pertanyaan: haruskah kode dikompilasi dan apa alasannya kode tersebut tidak dikompilasi oleh gcc dan dentang?

Vlad dari Moskow
sumber
Catatan: pada contoh pertama, meneruskan g(alih-alih &g) ke templat fungsi menyebabkan peluruhan tipe (referensi nilai fungsi meluruh ke penunjuk ke fungsi: void(&)(T)=> void(*)(T)). Konversi tersirat ini terjadi karena tidak ada fkelebihan lainnya dengan kecocokan yang lebih baik. Pada contoh kedua, ada ambiguitas yang fingin Anda panggil karena ... tidak tahu yang mana fargumennya.
Xeverous

Jawaban:

7

Sepertinya saya bahwa gcc dan dentang benar. Ini seharusnya tidak dikompilasi. Parameter fungsi dari mana Anda ingin Tdideduksi menjadi konteks non-deduksi di sini saat argumen yang diberikan adalah kumpulan kelebihan yang berisi templat fungsi [temp.deduct.type] /5.5 :

Konteks yang tidak dideduksi adalah:

  • [...]
  • Parameter fungsi yang pengurangan argumennya tidak dapat dilakukan karena argumen fungsi yang terkait adalah fungsi, atau sekumpulan fungsi kelebihan beban ([over.over]), dan satu atau lebih dari yang berikut ini berlaku:

    • [...]
    • set fungsi yang disediakan sebagai argumen berisi satu atau beberapa templat fungsi.
  • [...]

Dengan demikian, Ttidak dapat disimpulkan dan kelebihan lainnya tidak layak karena tidak ada konversi; persis apa yang dikatakan gcc ...

Michael Kenzel
sumber
0

Ini adalah dua fungsi kelebihan beban dan fungsi non-templat harus dipilih dibandingkan dengan fungsi templated, jadi f (int x) dipilih maka melewati fungsi sebagai argumen dalam fungsi yang int harus dilewati adalah mustahil. dan di bawah ini akan berfungsi. Terima kasih

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

  int main(){

     f( f );
  }
Lexshard
sumber
Fungsi non-templat lebih disukai hanya jika ada ikatan selama resolusi kelebihan beban. Kode pertanyaan tidak sampai ke titik itu: tidak pernah ada spesialisasi yang void f<int>(void(int))dihasilkan untuk melakukan resolusi kelebihan pada kedua level.
Davis Herring