11

Consider the following program.

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

The program compiles successfully and its output is

g( 42 );

Now let's rename the non-template function g to 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 );
}

Now the program is not compiled by gcc HEAD 10.0.0 20200 and clang HEAD 10.0.0 but compiled successfully by Visual C++ 2019..

For example the compiler gcc issues the following set of messages.

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

So a question arises: should the code be compiled and what is the reason that the code is not compiled by gcc and clang?

R2RT
  • 2,061
  • 15
  • 25
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • [See for yourself on godbolt](https://godbolt.org/z/cRGJ-k) – Zereges Jan 17 '20 at 13:04
  • Note: in the first example, passing `g` (instead of `&g`) to the function template causes a type decay (a function lvalue reference decays to a pointer to a function: `void(&)(T)` => `void(*)(T)`). This implicit convertion happens because there is no other `f` overload with a better match. In the second example, there is an ambiguity which `f` you want to actually call because ... it doesn't know which `f` is the argument either. – Xeverous Jan 20 '20 at 22:34

2 Answers2

7

It would seem to me that gcc and clang are correct. This should not compile. The function parameter from which you'd like T to be deduced becomes a non-deduced context here the moment the argument supplied is an overload set that contains a function template [temp.deduct.type]/5.5:

The non-deduced contexts are:

  • […]
  • A function parameter for which argument deduction cannot be done because the associated function argument is a function, or a set of overloaded functions ([over.over]), and one or more of the following apply:

    • […]
    • the set of functions supplied as an argument contains one or more function templates.
  • […]

Thus, T cannot be deduced and the other overload is not viable due to there being no conversion; exactly what gcc says…

Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
0

These are two overloaded functions and non-template function is should be selected compare to templated function, so f(int x) was selected hence passing a function as an argument in the function which int must be passed is impossible . and the below should work. Thanks

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

  int main(){

     f( f );
  }
Owl66
  • 147
  • 12
  • Non-template functions are preferred only when there is otherwise a tie during overload resolution. The question’s code doesn’t get to that point: there is never a specialization `void f(void(int))` generated so as to do overload resolution at *either* level. – Davis Herring Jan 17 '20 at 18:28