I have tried hard to make a lambda function return a value by reference without making a copy of the referenced value. My code example below illustrates the problem. It compiles and runs ok, but with the "//" commented line instead of the line above, it doesn't. I have found two workarounds (both illustrated in my example):
But both workarounds are not what I really want, and I do not understand why they are necessary: The expression "makeRefA()" has already the type the lambda function returns (const A&) and thus must neither be copied nor converted. By the way: The copy constructor is really called when I do not explicitly delete it (which in my "real" code is a performance problem). To me it looks like a compiler bug, but I have tried with several C++11-compilers which all show up the same error. So is there something special concerning the "return" statement in a lambda function?
#include <functional>
#include <iostream>
struct A {
A(int i) : m(i) { }
A(const A&) = delete;
int m;
};
void foo(const A & a) {
std::cout << a.m <<'\n';
}
const A & makeRefA() {
static A a(3);
return a;
}
int main() {
std::function<const A&()> fctRef = [&]
{ return std::ref(makeRefA()); }; //compiles ok
//{ return makeRefA(); }; //error: use of deleted function 'A::A(const A&)'
foo(fctRef());
std::function<const A*()> fctPtr =
[&] { return &makeRefA(); };
foo(*fctPtr());
return 0;
}
output:
3
3
By default, the automatically-deduced type of a lambda is the non-reference version of a type
... the return type is the type of the returned expression (after lvalue-to-rvalue, array-to-pointer, or function-to-pointer implicit conversion); (source)
If you want a return type with a reference, you will have to specify it more explictly. Here are some options:
[&]()
-> decltype( makeRefA() )
{ return makeRefA()); };
or simply be fully explicit about the return type with ->
[&]()
-> const A&
{ return makeRefA(); }
If using C++14, then simply use decltype(auto)
,
[&]()
-> decltype(auto)
{ return makeRefA(); }
The rules for decltype
can be complicated at times. But the fact that makeRefA()
is an expression (as opposed to simply naming a variable) means that the type of the expression (const A&
) is faithfully returned by decltype( makeRefA() )
.
You can specify the return type
#include <functional>
#include <iostream>
struct A {
A(int i) : m(i) { }
A(const A&) = delete;
int m;
};
void foo(const A & a) {
std::cout << a.m <<'\n';
}
const A & makeRefA() {
static A a(3);
return a;
}
int main() {
std::function<const A&()> fctRef = [&]()->const A&
// { return std::ref(makeRefA()); }; //compiles ok
{ return makeRefA(); }; // works
foo(fctRef());
std::function<const A*()> fctPtr =
[&] { return &makeRefA(); };
foo(*fctPtr());
return 0;
}
According to http://en.cppreference.com/w/cpp/language/lambda, these rules apply to lambdas with no trailing return type:
auto
; and that in turn follows the rules for template argument deduction. Then, since auto
includes no reference specification, that means that references and cv-qualifiers will be ignored.The effect is probably desirable in most situations: for example, in this lambda expression
[](const std::vector<int>& v) { return v[0]; }
you probably intend to return an int
, even though std::vector<int>::operator[] const
returns const int&
.
As others have mentioned, you can override this behavior by giving an explicit trailing return type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With