延长临时工的寿命

时间:2011-01-12 14:52:49

标签: c++ reference const temporary rvalue

允许这个

背后的设计理由是什么?
const Foo& a = function_returning_Foo_by_value();

但不是这个

Foo& a = function_returning_Foo_by_value();

第二行可能出现什么问题(第一行不会出错)?

4 个答案:

答案 0 :(得分:4)

我会回答你的问题......反过来说。

为什么他们允许Foo const& foo = fooByValue();开始?

它使生活(有些)更容易,但在整个地方引入了潜在的未定义行为。

Foo const& fooByReference()
{
  return fooByValue(); // error: returning a reference to a temporary
}

这显然是错误的,实际上编译器会尽职尽责地报告它。 根据Tomalak的评论:标准没有强制要求,但是好的编译器应该报告它。 Clang,gcc和MSVC呢。我认为Comeau和icc也是如此。

Foo const& fooByIndirectReference()
{
  Foo const& foo = fooByValue(); // OK, you're allowed to bind a temporary
  return foo;                    // Generally accepted
}

这是错误的,但更微妙。问题是临时的生命周期绑定到foo的生命周期,该生命周期超出了函数末尾的范围。 foo副本传递给调用者,此副本指向以太。

我提出了Clang的错误,而Argyris能够诊断出这个案例(真的很值得:p。)。

Foo const& fooForwarder(Foo const&); // out of line implementation which forwards
                                     // the argument

Foo const& fooByVeryIndirectReference()
{
  return fooForwarder(fooByValue());
}

fooByValue创建的临时值绑定到fooForwarder参数的生命周期,该参数尽职地提供(引用的)副本,即返回给调用者的副本,即使它现在指向以太。

这里的问题是fooForwarder的实现完全符合标准,但它在调用者中创建了未定义的行为。

令人生畏的事实是,诊断这一点需要了解fooForwarder的实现,这对编译器来说是遥不可及的。

我能理解的唯一解决方案(除了WPA)是一个运行时解决方案:只要临时绑定到引用,那么您需要确保返回的引用不共享相同的地址...然后是什么? assert?提出异常?由于它只是一个运行时解决方案,显然不能令人满意。

将临时绑定到引用的想法很脆弱。

答案 1 :(得分:3)

非常量指针不会延长临时值的生命周期的原因是非const引用不能首先绑定到临时值。

有很多原因,我只会展示一个涉及隐式扩展转换的经典示例:

struct Foo {};
bool CreateFoo( Foo*& result ) { result = new Foo(); return true; }

struct SpecialFoo : Foo {};
SpecialFoo* p;
if (CreateFoo(p)) { /* DUDE, WHERE'S MY OBJECT! */ }

允许const引用绑定临时对象的基本原理是它能够实现完全合理的代码:

bool validate_the_cat(const string&);

string thing[3];
validate_the_cat(thing[1] + thing[2]);

请注意,在这种情况下不需要终身扩展。

答案 2 :(得分:3)

我理解的理由如下:当一个临时超出范围时,预计会被销毁。 如果你保证不会修改它,我会让你延长它的寿命。

答案 3 :(得分:0)

“可能出错的地方”是您修改对象然后立即丢失更改,因此定义规则以帮助您不犯这样的错误。您可能会认为如果再次调用该函数,您将获得一个包含更改的对象,当然您不会因为修改了副本而对其进行了修改。

你要创建一个临时的,然后调用非const方法的典型情况是你打算交换它:

std::string val;
some_func_that_returns_a_string().swap( val );

这有时非常有用。