Рассмотрим следующую программу.
#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?
c++
templates
language-lawyer
c++17
template-argument-deduction
Влад из Москвы
источник
источник
g
(вместо&g
) в шаблон функции вызывает затухание типа (ссылка на lvalue функции затухает до указателя на функцию:void(&)(T)
=>void(*)(T)
). Это неявное преобразование происходит потому, что нет другойf
перегрузки с лучшим соответствием. Во втором примере есть неопределенность, которуюf
вы хотите вызвать, потому что ... она также не знает, какойf
аргумент есть.Ответы:
Мне кажется, что gcc и clang верны. Это не должно компилироваться. Параметр функции, из которого вы хотите
T
получить вывод, становится здесь не выводимым контекстом в тот момент, когда предоставленный аргумент является набором перегрузки, который содержит шаблон функции [temp.deduct.type] /5.5 :Таким образом,
T
не может быть выведено, и другая перегрузка не является жизнеспособной из-за отсутствия преобразования; именно то, что говорит GCC ...источник
Это две перегруженные функции, и не шаблонная функция должна быть выбрана по сравнению с шаблонной функцией, поэтому была выбрана функция f (int x), поэтому передача функции в качестве аргумента в функцию, которую int необходимо передать, невозможна. и ниже должно работать. Спасибо
источник
void f<int>(void(int))
для разрешения перегрузки на любом уровне.