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

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 );
  }
Lexshard
источник
Не шаблонные функции предпочтительны только тогда, когда во время разрешения перегрузки есть связь. Код вопроса не доходит до этой точки: никогда не создается специализация void f<int>(void(int))для разрешения перегрузки на любом уровне.
Дэвис Херринг