James Thrush
James Thrush

Reputation: 46

Overloading a function that takes a std::function with a different function signature results in compiler error

I came across this compiler/language issue while working on a callback system. I'm using Visual Studio 2012. If I write:

// Works
template<typename T>
class C
{
};
C<void(int)>        a1;
C<void(int, int)>   a2;
void Good(C<void(int)>)         { }
void Good(C<void(int, int)>)    { }

void main()
{
    Good(a1);
    Good(a2);
}

it compiles cleanly and I verified in the debugger that the correct overloads are being called.

However, if I write:

void Foo(std::function<void(int)> fn)   { }
void Foo(std::function<void(int, int)> fn)  { } //<--- breaks compilation
void Bar(int a) { }

void main()
{
    Foo(Bar);
}

I get the following:

>c:\dev\newtech2\src\game.cpp(389): error C2668: 'Foo' : ambiguous call to overloaded function
>          c:\dev\newtech2\src\game.cpp(380): could be 'void Foo(std::function<_Fty>)'
>          with
>          [
>              _Fty=void (int,int)
>          ]
>          c:\dev\newtech2\src\game.cpp(379): or       'void Foo(std::function<_Fty>)'
>          with
>          [
>              _Fty=void (int)
>          ]
>          while trying to match the argument list '(overloaded-function)'

The compiler clearly knows that the two functions have different signatures due to the different template parameters, and it was able to distinguish the two in the previous example using my own template class. So is this a compiler/STL bug or some limitation in how std::function() works? Not a show-stopper but does limit one type of callback interface I wanted to provide.

Upvotes: 0

Views: 51

Answers (1)

Lingxi
Lingxi

Reputation: 14967

In the example that works, types of the arguments a1 and a2 match exactly the corresponding parameter types of the two Good overloads. This is not true to the second example. The selected constructor of std::function is a function template, and has little to do with the function signature you supplied whatsoever. That is, in the eye of the compiler, to (try to) convert Bar to std::function<void(int)> is no better than to convert it to std::function<void(int, int)>, despite the fact that choosing the second will ultimately lead to a compiler error (the compiler cannot foresee this anyway). One way to remedy the situation is to explicitly cast the argument Bar to the appropriate std::function type, or to make a convenient converter wrapper yourself to automate this procedure.

Upvotes: 1

Related Questions