The assignment operator is defined to have the effect:
function(std::forward<F>(f)).swap(*this);
(14882:2011 20.8.11.2.1 par. 18)
The constructor that this references,
template <class F> function(F f);
requires:
F
shall beCopyConstructible
.f
shall beCallable
(20.8.11.2) for argument typeArgTypes
and return typeR
.
where Callable
is defined as follows:
A callable object
f
of typeF
isCallable
for argument typesArgTypes
and return typeR
if the expressionINVOKE(f, declval<ArgTypes>()..., R)
, considered as an unevaluated operand (Clause 5), is well formed (20.8.2).
INVOKE
, in turn, is defined as:
Define INVOKE(f, t1, t2, ..., tN) as follows:
- ... cases for handling member functions omitted here ...
f(t1, t2, ..., tN)
in all other cases.Define
INVOKE(f, t1, t2, ..., tN, R)
asstatic_cast<void>(INVOKE(f, t1, t2, ..., tN))
ifR
is cvvoid
, otherwiseINVOKE(f, t1, t2, ..., tN)
implicitly converted toR
.
Since the INVOKE
definition becomes a plain function call, in this case, the arguments can be converted: If your std::function<void(const int&)>
accepts a const int&
then it can be converted to an int
for the call. The example below compiles with clang++ -std=c++14 -stdlib=libc++ -Wall -Wconversion
:
int main() { std::function<void(const int& i)> f; f = [](int i) -> void { std::cout << i << std::endl; }; f(2); return 0;}
The return type (void
) is handled by the static_cast<void>
special-case for the INVOKE
definition.
Note, however, that at the time of writing the following generates an error when compiled with clang++ -std=c++1z -stdlib=libc++ -Wconversion -Wall
, but not when compiled with clang++ -std=c++1z -stdlib=libstdc++ -Wconversion -Wall
:
int main() { std::function<void(const int& i)> f; f = [](int i) -> int { std::cout << i << std::endl; return i;}; f(2); return 0;}
This is due to libc++
implementing the behaviour as specified in C++14, rather than the amended behaviour described above (thanks to @Jonathan Wakely for pointing this out). @Arunmu describes the libstdc++
type trait responsible for the same thing in his post. In this regard, implementations may behave slightly differently when handling callables with void
return types, depending on whether they implement C++11, 14, or something newer.