Reputation: 71
I have a compilation warning about returning a reference to a local object while using visit()
, but I fail to understand why...
I use the following code (it's a bit contrived, but this is for demonstration purposes):
#include <iostream>
template <class... Ts> struct overloaded : Ts... {
using Ts::operator()...;
};
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
using namespace std;
class B {
public:
B(int i) :
i_(i) {}
int get_i() const {
return i_;
}
private:
int i_;
};
class D: public B {
public:
D(int i) :
B(i) {}
};
const B& as_base(const variant<B, D>& op) {
return visit(overloaded {
[](const B& b) { return static_cast<const B&>(b); },
[](const D& d) {
cout << "in as_base: &d = " << &d << "\n";
const B& b = static_cast<const B&>(d);
cout << "in as_base: &b = " << &b << "\n";
return b;
}}, op);
}
auto as_base_lambda = [](const variant<B, D>& op) {
return visit(overloaded {
[](const B& b) { return static_cast<const B&>(b); },
[](const D& d) {
cout << "in as_base_lambda: &d = " << &d << "\n";
const B& b = static_cast<const B&>(d);
cout << "in as_base_lambda: &b = " << &b << "\n";
return b;
}}, op);
};
int main() {
variant<B, D> v(d);
const B& b1 = get<1>(v);
const B& b2 = as_base(v);
const B& b3 = as_base_lambda(v);
cout << "&b1 = " << &b1 << " - i = " << b1.get_i() << "\n";
cout << "&b2 = " << &b2 << " - i = " << b2.get_i() << "\n";
cout << "&b3 = " << &b3 << " - i = " << b3.get_i() << "\n";
}
When I compile (clang++ 17, gcc++ 13), I get this warning:
test.cpp:33:12: warning: returning reference to local temporary object [-Wreturn-stack-address]
33 | return visit(overloaded {
| ^~~~~~~~~~~~~~~~~~
34 | [](const B& b) { return static_cast<const B&>(b); },
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35 | [](const D& d) {
| ~~~~~~~~~~~~~~~~
36 | cout << "in as_base: &d = " << &d << "\n";
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37 | const B& b = static_cast<const B&>(d);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
38 | cout << "in as_base: &b = " << &b << "\n";
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
39 | return b;
| ~~~~~~~~~
40 | },
| ~~
41 | }, op);
So the as_base()
function apparently creates some temporary and returns a reference to it. But where? what temporary are we talking about?
What I don't understand is that the seemingly equivalent function as_base_lambda()
has no problem.
If I run the code nonetheless, I get:
# here is the address of the d object in the variant
&b1 = 0x7fff02fb55f8 - i = 5
# the visitors in as_base() and as_base_lambda() receive the same object (same address);
# note also that the b object has the same address as d, as expected:
in as_base: &d = 0x7fff02fb55f8
in as_base: &b = 0x7fff02fb55f8
in as_base_lambda: &d = 0x7fff02fb55f8
in as_base_lambda: &b = 0x7fff02fb55f8
# however the final object returned by as_base() is different, and apparently
# plain wrong (the value i is off):
&b2 = 0x7fff02fb5594 - i = 21880
# the final object returned by as_base_lambda() is yet again different, but its value
# is correct...
&b3 = 0x7fff02fb55d4 - i = 5
I have trouble understanding what's going on here: is it visit() that creates a temporary, and eventually returns its address? but why is as_base_lambda() legitimate, and not as_base()?
Interestingly, if I modify as_lambda()
to return a pointer instead:
const B* as_base(const variant<B, D>& op) {
return visit(overloaded {
[](const B& b) { return static_cast<const B*>(&b); },
[](const D& d) { return static_cast<const B*>(&b); }
}}, op);
}
Then I have no warning, and it "works"...
Upvotes: 1
Views: 68
Reputation: 117937
Your lambas will return B
unless you specify the type to be const B&
. Lambdas are implicitly auto
and auto
is never a reference unless you explicitly specify it with auto&
.
const B& as_base(const variant<B, D>& op) {
return visit(overloaded{[](const B& b) -> const B& { return b; }, // or auto&
[](const D& d) -> const B& { return d; }},
op);
}
if I modify
as_lambda()
to return a pointer instead [...] it "works"
Yes, a plain auto
can be deduced to be of pointer type - but not of reference type - unless you use auto&
/auto&&
or decltype(auto)
(since C++14).
Upvotes: 5