Consider this example of code:
#include
#include
typedef std::function func1_t;
typedef std::function
In C++11...
Let's take a look at the specification of the constructor template of std::function
(which takes any Callable): [func.wrap.func.con]/7-10
template<class F> function(F f); template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires:
F
shall beCopyConstructible
.f
shall beCallable
(20.10.11.2) for argument typesArgTypes
and return typeR
. The copy constructor and destructor ofA
shall not throw exceptions.8 Postconditions:
!*this
if any of the following hold:
f
is aNULL
function pointer.f
is aNULL
pointer to member.F
is an instance of the function class template, and!f
9 Otherwise,
*this
targets a copy off
initialized withstd::move(f)
. [left out a note here]10 Throws: shall not throw exceptions when
f
is a function pointer or areference_wrapper<T>
for someT
. Otherwise, may throwbad_alloc
or any exception thrown byF
’s copy or move constructor.
Now, constructing, or attempting to construct (for overload resolution) a std::function<void(int)>
from a [](){}
(i.e. with signature void(void)
) violates the requirements of std::function<void(int)>
's constructor.
[res.on.required]/1
Violation of the preconditions specified in a function’s Requires: paragraph results in undefined behavior unless the function’s Throws: paragraph specifies throwing an exception when the precondition is violated.
So, AFAIK, even the result of the overload resolution is undefined. Therefore, both versions of g++/libstdc++ are complying in this aspect.
In C++14, this has been changed, see LWG 2132. Now, the converting constructor template of std::function
is required to SFINAE-reject incompatible Callables (more about SFINAE in the next chapter):
template<class F> function(F f); template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires:
F
shall beCopyConstructible
.8 Remarks: These constructors shall not participate in overload resolution unless
f
is Callable (20.9.11.2) for argument typesArgTypes...
and return typeR
.[...]
The "shall not participate in overload resolution" corresponds to rejection via SFINAE. The net effect is that if you have an overload set of functions foo
,
void foo(std::function<void(double)>);
void foo(std::function<void(char const*)>);
and a call-expression such as
foo([](std::string){}) // (C)
then the second overload of foo
is chosen unambiguously: Since std::function<F>
defines F
as its interface to the outside, the F
defines which argument types are passed into std::function
. Then, the wrapped function object has to be called with those arguments (argument types). If a double
is passed into std::function
, it cannot be passed on to a function taking a std::string
, because there's no conversion double
-> std::string
.
For the first overload of foo
, the argument [](std::string){}
is therefore not considered Callable for std::function<void(double)>
. The constructor template is deactivated, hence there's no viable conversion from [](std::string){}
to std::function<void(double)>
. This first overload is removed from the overload set for resolving the call (C), leaving only the second overload.
Note that there's been a slight change to the wording above, due to LWG 2420: There's an exception that if the return type R
of a std::function<R(ArgTypes...)>
is void
, then any return type is accepted (and discarded) for the Callable in the constructor template mentioned above. For example, both []() -> void {}
and []() -> bool {}
are Callable for std::function<void()>
. The following situation therefore produces an ambiguity:
void foo(std::function<void()>);
void foo(std::function<bool()>);
foo([]() -> bool {}); // ambiguous
The overload resolution rules don't try to rank among different user-defined conversions, and hence both overloads of foo
are viable (first of all) and neither is better.
Note when a SFINAE-check fails, the program isn't ill-formed, but the function isn't viable for overload resolution. For example:
#include <type_traits>
#include <iostream>
template<class T>
auto foo(T) -> typename std::enable_if< std::is_integral<T>::value >::type
{ std::cout << "foo 1\n"; }
template<class T>
auto foo(T) -> typename std::enable_if< not std::is_integral<T>::value >::type
{ std::cout << "foo 2\n"; }
int main()
{
foo(42);
foo(42.);
}
Similarly, a conversion can be made non-viable by using SFINAE on the converting constructor:
#include <type_traits>
#include <iostream>
struct foo
{
template<class T, class =
typename std::enable_if< std::is_integral<T>::value >::type >
foo(T)
{ std::cout << "foo(T)\n"; }
};
struct bar
{
template<class T, class =
typename std::enable_if< not std::is_integral<T>::value >::type >
bar(T)
{ std::cout << "bar(T)\n"; }
};
struct kitty
{
kitty(foo) {}
kitty(bar) {}
};
int main()
{
kitty cat(42);
kitty tac(42.);
}
It is totally valid. Since c++11 lambda expressions (and your std::function
wrapper) create function objects. The great strength of function objects is that, even when they are generic, they remain first-class objects. Unlike ordinary function templates, they can be passed to and returned from functions.
You can create operator overload sets explicitly with inheritance and using declarations. The following usage from Mathias Gaunard demonstrates “overloaded lambda expressions".
template <class F1, class F2>
struct overload_set : F1, F2
{
overload_set(F1 x1, F2 x2) : F1(x1), F2(x2) {}
using F1::operator();
using F2::operator();
};
template <class F1, class F2>
overload_set<F1,F2> overload(F1 x1, F2 x2)
{
return overload_set<F1,F2>(x1,x2);
}
auto f = overload(
[](){return 1;},
[](int x){return x+1;}
);
int x = f();
int y = f(2);
source
EDIT: Maybe it'll become more clear if in the provided example you replace
F1 -> std::function<void()>
F2 -> std::function<void(int)>
and see it compile in gcc4.7
The templated solution was only provide to demonstrate that concept scales to generic code and dissambiguation is possible.
In your case, when using an older compiler like gcc 4.7, you could help by explicit cast and gcc will work things out, as you can see in this live example
Just in case you're wondering, it wouldn't work if you cast the other way around (try to convert the lambda taking int to the std::function taking no arguments and so on)