返回*作为参考是否安全?

ant*_*_rh 18 c++ reference this this-pointer

返回对此对象的引用通常用于赋值运算符重载.它还用作命名参数idiom的基础,它允许通过对setter方法的调用链初始化对象:Params().SetX(1).SetY(1)每个都返回对*this的引用.

但是返回引用是否正确*this.如果我们调用该方法为临时对象返回对此的引用,该怎么办:

#include <iostream>

class Obj
{
public:
    Obj(int n): member(n) {}
    Obj& Me() { return *this; }

    int member;
};

Obj MakeObj(int n)
{
    return Obj(n);
}

int main()
{
    // Are the following constructions are correct:
    std::cout << MakeObj(1).Me().member << std::endl;
    std::cout << Obj(2).Me().member << std::endl;
    Obj(3).Me() = Obj(4);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

JVA*_*pen 8

是的,返回*这是安全的.简单的情况是,这不是暂时的,但即使它是,这应该是可能的:

临时对象作为评估全表达式(1.9)的最后一步被销毁,该表达式(词法上)包含创建它们的点.即使该评估以抛出异常结束也是如此(C++03§12.2/ 3).

换句话说,直到你达到分号,一切都应该没问题(理论上).

所以下面的代码应该工作:

std::cout << MakeObj(1).Me().member << std::endl;
Run Code Online (Sandbox Code Playgroud)

虽然这不起作用:

const Obj &MakeMeObj(int n) { return Obj(n).Me(); }
std::cout << MakeMeObj(1).member << std::endl;
Run Code Online (Sandbox Code Playgroud)

这是合乎逻辑的,因为您返回对临时的引用.大多数编译器都会对此发出警告/错误,但如果您的代码变得复杂,则需要注意这一点.

就个人而言,我会阻止在临时对象上调用这些方法来强制API用户考虑对象的生命周期.可以通过重载方法来完成:(如果您的编译器已经支持它)

Obj &Me() & { return *this; }
Obj &Me() && = delete;
Run Code Online (Sandbox Code Playgroud)

  • 我认为它是C++ 11,但至少MSVC2013不支持.从我在http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2439.htm中可以读到的内容,我将其称为函数重载在此-ptr的ref-qualifier上,don不知道正式的名字. (2认同)

Chr*_*phe 5

是的,这很安全。临时对象的生命周期将持续到语句结束(更准确地说是创建它的完整表达式的求值)。这是由标准保证的:

12.2/3:临时对象在评估(词法上)包含它们创建点的完整表达式的最后一步被销毁。

如果绑定到参考,在某些情况下甚至可以延长临时寿命。但不要指望这里会出现奇迹。尝试将引用保留在语句之外(例如,通过获取地址或分配引用)可能很快会导致 UB(演示)。

如果您在对象上使用这种构造,const您也会遇到一些麻烦,因为您会尝试返回非const引用(但这与您的赋值和设置器示例无关)。


Chr*_*ckl 5

// Are the following constructions are correct:
std::cout << MakeObj(1).Me().member << std::endl;
std::cout << Obj(2).Me().member << std::endl;
Run Code Online (Sandbox Code Playgroud)

是的,因为在每一行中,所有临时对象的生存期都得到了扩展,以考虑到完整的表达式

正如cppreference.com所说:

(...)所有临时对象都被销毁,这是评估(按词法)包含创建它们的位置的完整表达式(...)的最后一步。

如果尝试拆分完整表达式,则(希望)会收到编译器错误或警告:

// not allowed:
Obj& ref = MakeObj(1);
std::cout << ref.Me().member << std::endl;
Run Code Online (Sandbox Code Playgroud)

在其他情况下,编译器可能不够聪明,无法看到问题,没有给出任何诊断消息就创建了可执行文件,并最终在程序中构建了未定义的行为:

// undefined behaviour:
Obj &ref = MakeObj(1).Me();
std::cout << ref.member << std::endl;
Run Code Online (Sandbox Code Playgroud)