简体   繁体   English

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

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

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. 特别是在std::vector情况下,我得到这个警告会非常棘手,因为编译器并不明显std::vector<int>::operator[](size_t) const返回的const int&是一个引用到临时。 I'm actually a little surprised that std::array doesn't fail, though, since this similar case does give me an error: 我实际上有点惊讶std::array没有失败,因为这个类似的情况确实给了我一个错误:

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: 我用以下内容绊倒了这个,其中我内联了一系列链接调用,但是因为C ++允许你将locals分配给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;
}

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

the verbose version works while the superficially-identical version deletes the temporary right away, leaving a dangling reference 详细版本可以正常工作,而表面相同的版本会立即删除临时版本,留下悬空参考

Your code is not identical at all. 您的代码完全不相同。 At the first case: 在第一种情况:

const auto& a = c.a();
const auto& x = a.get();

lifetime of temporary extended for lifetime of const reference, so x is valid as long as a is valid, but in the second: 临时扩展的生命周期为const引用的生命周期,因此只要a有效, x就有效,但在第二个:

const int& x = c.a().get();

you have dangling reference x . 你有悬挂参考x And the case you have here is unrelated to examples you shown before - when you return a dangling reference to a local variable hence warnings you are looking in examples almost unrelated if a compiler would detect situation you described in real code. 你在这里的情况与你之前显示的例子无关 - 当你返回一个对本地变量的悬空引用时,因此警告你正在查看几乎不相关的例子,如果编译器会检测到你在实际代码中描述的情况。

Solution for your case though can be made by designer of the class A : 虽然可以由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
};

Using a value whose life has ended is undefined behavior, see [basic.life]/6.1 . 使用寿命已结束的值是未定义的行为,请参阅[basic.life] /6.1 The standard does not require the compiler to output any diagnostics for UB. 该标准不要求编译器输出任何 UB诊断。

So the diagnostics you're seeing are just a courtesy of the compiler. 因此,您所看到的诊断只是对编译器的礼貌。 It's nice to see you're getting some of those, but they are certainly far from watertight as you have noticed. 很高兴看到你得到了其中的一些,但是你注意到它们肯定远非防水。

And yes, lifetime extension isn't chainable. 是的,终身延伸不可链接。 That makes it very dangerous and unreliable. 这使得它非常危险和不可靠。

You can try Clang's Address Sanitizer (ASAN). 您可以尝试Clang的Address Sanitizer(ASAN)。

In fact ASAN seems to be catching your issue ( -fsanitize-address-use-after-scope ): 事实上,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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM