There's an error for this case:
const int& foo() {
const int x = 0;
return x;
}
and even
const int& foo() {
const std::pair<int,int> x = {0,0};
return x.first;
}
but not this:
const int& foo() {
const std::array<int,1> x = {0};
return x[0];
}
and (less surprisingly) not this:
const int& foo() {
const std::vector<int> x = {0};
return x[0];
}
Particularly in the std::vector
case, I get that this warning would be pretty tricky, since it's not obvious to the compiler that the const int&
returned by std::vector<int>::operator[](size_t) const
is a reference into the temporary. I'm actually a little surprised that std::array
doesn't fail, though, since this similar case does give me an error:
struct X {
int x[0];
};
const int& foo() {
X x;
return x.x[0];
}
Do any of the popular compilers have a warning/error that can catch these cases? I could imagine a conservative version that would warn about returning a reference that came from a member-function call on a temporary.
I tripped over this with something like the following, in which I inlined a chained series of calls, but because C++ lets you assign locals to const&
, the verbose version works while the superficially-identical version deletes the temporary right away, leaving a dangling reference:
#include <iostream>
struct A {
int x = 1234;
A() { std::cout << "A::A " << this << std::endl; }
~A() { x = -1; std::cout << "A::~A " << this << std::endl; }
const int& get() const { return x; }
};
struct C {
C() { std::cout << "C::C " << this << std::endl; }
~C() { std::cout << "C::~C " << this << std::endl; }
A a() { return A(); }
};
int foo() {
C c;
const auto& a = c.a();
const auto& x = a.get();
std::cout << "c.a(); a.get() returning at " << &x << std::endl;
return x;
}
int bar() {
C c;
const int& x = c.a().get();
std::cout << "c.a().get() returning at " << &x << std::endl;
return x;
}
int main() {
std::cout << foo() << std::endl;
std::cout << bar() << std::endl;
}
That outputs
C::C 0x7ffeeef2cb68 A::A 0x7ffeeef2cb58 c.a(); a.get() returning at 0x7ffeeef2cb58 A::~A 0x7ffeeef2cb58 C::~C 0x7ffeeef2cb68 1234 C::C 0x7ffeeef2cb68 A::A 0x7ffeeef2cb58 A::~A 0x7ffeeef2cb58 c.a().get() returning at 0x7ffeeef2cb58 C::~C 0x7ffeeef2cb68 -1