是否有将引用返回到临时引用的C++警告?

Is there a C++ warning for returning a reference into a temporary?

本文关键字:引用 C++ 警告 返回 是否      更新时间:2023-10-16

这种情况有一个错误:

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