这种情况有一个错误:
const int& foo() {
const int x = 0;
return x;
}
甚至
const int& foo() {
const std::pair<int,int> x = {0,0};
return x.first;
}
但不是这个:
const int& foo() {
const std::array<int,1> x = {0};
return x[0];
}
而且(不那么令人惊讶(不是这个:
const int& foo() {
const std::vector<int> x = {0};
return x[0];
}
特别是在std::vector
的情况下,我得到这个警告会非常棘手,因为编译器并不明显地认为std::vector<int>::operator[](size_t) const
返回的const int&
是对临时的引用。不过,我实际上有点惊讶std::array
没有失败,因为这个类似的情况确实给了我一个错误:
struct X {
int x[0];
};
const int& foo() {
X x;
return x.x[0];
}
是否有任何流行的编译器具有可以捕获这些情况的警告/错误?我可以想象一个保守的版本,它会警告返回来自临时成员函数调用的引用。
我用如下所示的内容绊倒了这一点,其中我内联了一系列链接的调用,但由于C++允许您将本地人分配给const&
,因此详细版本有效,而表面上相同的版本会立即删除临时版本,留下悬而未决的引用:
#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;
}
输出
C::C 0x7ffeeef2cb68 答::0x7ffeeef2cb58 c.a((;a.get(( 返回 0x7ffeeef2cb58 答::~0x7ffeeef2cb58 C::~C 0x7ffeeef2cb68 1234 C::C 0x7ffeeef2cb68 答::0x7ffeeef2cb58 答::~0x7ffeeef2cb58 c.a((.get(( return at 0x7ffeeef2cb58 C::~C 0x7ffeeef2cb68 -1
详细版本有效,而表面上相同的版本立即删除临时版本,留下悬而未决的引用
您的代码根本不相同。在第一种情况下:
const auto& a = c.a();
const auto& x = a.get();
临时的生存期延长为常量引用的生存期,因此只要a
有效,x
就有效,但在第二个:
const int& x = c.a().get();
你有悬而未决的参考x
.您在这里遇到的情况与您之前显示的示例无关 - 当您返回对局部变量的悬空引用时,因此警告您正在查找几乎不相关的示例,如果编译器会检测到您在实际代码中描述的情况。
不过,您的情况的解决方案可以由类A
的设计师提出:
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; }
int get() && { return x; } // prevent dangling reference or delete it to prevent compilation
};
使用生命已结束的值是未定义的行为,请参阅 [basic.life]/6.1。 该标准不要求编译器输出 UB的任何诊断。
因此,您看到的诊断只是编译器的礼貌。很高兴看到你得到了其中的一些,但它们肯定远非你注意到的无懈可击。
是的,寿命延长是不可链接的。这使得它非常危险和不可靠。
您可以尝试Clang的地址清理器(ASAN(。
事实上,ASAN似乎正在抓住您的问题(-fsanitize-address-use-after-scope
(:
==35463==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fffffffe970 at pc 0x000000498d53 bp 0x7fffffffe910 sp 0x7fffffffe908
READ of size 4 at 0x7fffffffe970 thread T0