Вирахування аргументу шаблону для аргументу типу функції


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

Тепер перейменоваємо нешаблонну функцію gв 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 );
}

Тепер програма не компілюється 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) до шаблону функції викликає розпад типу (функція посилання lvalue зменшується на покажчик на функцію: void(&)(T)=> void(*)(T)). Це неявна конверсія відбувається тому, що немає іншого fперевантаження з кращою відповідністю. У другому прикладі є двозначність, яку fви хочете насправді викликати, тому що ... він також не знає, що fє аргументом.
Xeverous

Відповіді:


7

Мені здалося б, що gcc і clang є правильними. Це не повинно складатись. Параметр функції, з якого ви хочете Tвивести, стає невиведеним контекстом, коли момент, коли поданий аргумент, є набором перевантаження, що містить шаблон функції [temp.deduct.type] /5.5 :

Невиведені контексти:

  • […]
  • Параметр функції, для якого виведення аргументів неможливо зробити, оскільки пов'язаний аргумент функції є функцією або набором перевантажених функцій ([over.over]), і застосовується одне або більше з наступного:

    • […]
    • набір функцій, що подається як аргумент, містить один або більше шаблонів функцій.
  • […]

Таким чином, Tне можна вивести, а інша перевантаження не є життєздатною через відсутність перетворення; саме те, що говорить gcc ...


0

Це дві перевантажені функції, і не шаблонна функція повинна бути обрана порівняно з шаблонною функцією, тому 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))для вирішення перевантаження на будь-якому рівні.
Девіс Оселедець
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.