Sorry. I don't get what you said |
No worries, I gave a brief answer just to point out a subtlety.
Consider this simpler example:
1 2 3 4 5
|
void f(double const& x);
void g(double y);
int i = 0;
f(i);
g(i);
|
The type of
i is
int, not
double. Therefore, when
i is provided as an argument to
f, the compiler must convert the argument from
int to
double.
This process is called
implicit conversion. The result of the conversion is a value (specifically, a
prvalue) with type
double, which is used to initialize the parameter
x.
A similar process occurs when passing
i to
g. An expression of type
int was provided where a double was expected, so the compiler must convert
int to
double. The result of the conversion is used to initialize the parameter
y.
The situation you've provided is similar:
The compiler writes a new class for each lambda expression that appears in the code - therefore, the type of
count is not
std::function<void(void)>. Instead, its type is unique - a compiler-generated class type that we'll call
ClosureType.
When you pass
count to
invokeV1 (by-value), you've provided an argument with type
ClosureType where a
std::function<void(void)> was expected, so a conversion is required.
In order to do so, the compiler implicitly instantiates and calls
std::function<void(void)>'s constructor template, which has the signature
template<typename F> function(F f);
The constructor that's finally called
a.) is non-
explicit; and
b.) has one argument.
Such constructors are called
converting constructors. Converting constructors are the only constructors suitable for use by the compiler during implicit conversions.
The constructor produces a
std::function<void(void)> that wraps a
copy of the function object
count. The result is used to initialize the parameter
fn. When
fn's finally called, it doesn't affect the original object at all. Note that the copy of
count is made by
std::function's constructor during the implicit conversion, not by your code.
When you pass
count to
invokeV2 (by-reference), the situation's the same as the last paragraph.
If you declared
count as
1 2 3
|
std::function<void(void)> count{ [i]() mutable {
std::cout << ++i << '\n';
} };
|
then no conversion step is required - so no copy is made unless you pass by value. If you pass
count by reference, the changes to the state of
count will be reflected throughout the program.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
#include <iostream>
#include <functional>
using fn_type = std::function<void(void)>;
void call_std_fn_passed_by_const_reference (fn_type const& fn)
{ std::cout << "std::function const&: "; fn(); }
void call_std_fn_passed_by_mutable_reference(fn_type& fn)
{ std::cout << "std::function&: "; fn(); }
void call_std_fn_passed_by_value (fn_type fn)
{ std::cout << "std::function: "; fn(); }
template <typename Fn> void call_fn_by_value(Fn fn)
{ std::cout << "F: "; fn(); }
int main()
{
{
fn_type fn { [i=0]() mutable { std::cout << (++i) << '\n'; } };
call_std_fn_passed_by_const_reference(fn);
call_std_fn_passed_by_mutable_reference(fn);
call_std_fn_passed_by_value(fn);
call_fn_by_value(fn);
std::cout << '\n';
}
{
auto fn { [i=0]() mutable { std::cout << (++i) << '\n'; } };
call_std_fn_passed_by_const_reference(fn);
// error: result of conversion is not an lvalue
// call_std_fn_passed_by_mutable_reference(fn);
call_std_fn_passed_by_value(fn);
call_fn_by_value(fn);
std::cout << '\n';
}
{
fn_type fn { [i=0]() mutable { std::cout << (++i) << '\n'; } };
call_std_fn_passed_by_const_reference(std::ref(fn));
call_std_fn_passed_by_mutable_reference(std::ref(fn));
call_std_fn_passed_by_value(std::ref(fn));
call_fn_by_value(std::ref(fn));
std::cout << '\n';
}
{
auto fn { [i=0]() mutable { std::cout << (++i) << '\n'; } };
call_std_fn_passed_by_const_reference(std::ref(fn));
// error: not an lvalue
// call_std_fn_passed_by_mutable_reference(std::ref(fn));
call_std_fn_passed_by_value(std::ref(fn));
call_fn_by_value(std::ref(fn));
std::cout << '\n';
}
}
|
http://coliru.stacked-crooked.com/a/9d49af2642b10d5f