Dedução de argumento de modelo para um argumento de um tipo de função

10

Considere o seguinte programa.

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

O programa é compilado com sucesso e sua saída é

g( 42 );

Agora vamos renomear a função não modelo gpara 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 );
}

Agora, o programa não é compilado pelo gcc HEAD 10.0.0 20200 e clang HEAD 10.0.0, mas compilado com sucesso pelo Visual C ++ 2019 ..

Por exemplo, o compilador gcc emite o seguinte conjunto de mensagens.

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

Portanto, surge uma pergunta: o código deve ser compilado e qual é a razão pela qual o código não é compilado pelo gcc e pelo clang?

Vlad de Moscou
fonte
Nota: no primeiro exemplo, passar g(em vez de &g) para o modelo de função causa uma deterioração de tipo (uma referência de valor da função decai para um ponteiro para uma função: void(&)(T)=> void(*)(T)). Essa conversão implícita acontece porque não há outra fsobrecarga com uma correspondência melhor. No segundo exemplo, há uma ambiguidade que fvocê deseja chamar de fato porque ... também não sabe qual fé o argumento.
Xeverous 20/01

Respostas:

7

Parece-me que gcc e clang estão corretos. Isso não deve compilar. O parâmetro de função do qual você deseja Tdeduzir se torna um contexto não deduzido aqui no momento em que o argumento fornecido é um conjunto de sobrecargas que contém um modelo de função [temp.deduct.type] /5.5 :

Os contextos não deduzidos são:

  • [...]
  • Um parâmetro de função para o qual a dedução de argumento não pode ser feita porque o argumento da função associada é uma função ou um conjunto de funções sobrecarregadas ([over.over]), e um ou mais dos seguintes itens se aplicam:

    • [...]
    • o conjunto de funções fornecidas como argumento contém um ou mais modelos de funções.
  • [...]

Assim, Tnão pode ser deduzido e a outra sobrecarga não é viável por não haver conversão; exatamente o que o gcc diz…

Michael Kenzel
fonte
0

Essas são duas funções sobrecarregadas e a função não modelo deve ser selecionada em comparação com a função modelada; portanto, f (int x) foi selecionado, portanto, é impossível passar uma função como argumento na função na qual int deve ser passado. e o abaixo deve funcionar. obrigado

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

  int main(){

     f( f );
  }
Lexshard
fonte
As funções que não são do modelo são preferidas apenas quando houver um empate durante a resolução de sobrecarga. O código da pergunta não chega a esse ponto: nunca há uma especialização void f<int>(void(int))gerada para executar a resolução de sobrecarga nos dois níveis.
Davis Herring