【问题标题】:prolonging the lifetime of temporaries延长临时工的寿命
【发布时间】:2011-06-07 21:28:27
【问题描述】:

允许这样做的设计原理是什么

const Foo& a = function_returning_Foo_by_value();

但不是这个

Foo& a = function_returning_Foo_by_value();

?

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

【问题讨论】:

  • 这不是 Herb Sutter 在这里herbsutter.com/2008/01/01/… 讨论的同一个问题吗?
  • @DumbCoder:不,Herb Sutter 根据 C++ 标准设计用途,而 Fred 讨论标准背后的基本原理。

标签: c++ reference constants temporary rvalue


【解决方案1】:

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

为什么他们允许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 ?引发异常?而且由于它只是一个运行时解决方案,显然不能令人满意。

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

【讨论】:

  • “这显然是错误的,编译器确实会尽职尽责地报告它”如果你很幸运,如果你有一个工具链可以做到这一点,如果你的警告设置为某个级别等等. C++ 语言不要求对这种情况进行诊断。
  • @Tomalak:我会纠正的,至少 MSVC、gcc 和 Clang 都报告了,我认为 Comeau 和 icc 可能也会。
【解决方案2】:

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

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

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]);

请注意,在这种情况下不需要延长生命周期。

【讨论】:

  • 这段代码展示的问题是右值不应该绑定到普通引用。它没有说明为什么右值应该绑定到 const 引用。爱这个家伙,我的对象在哪里:)
  • @David: const Foo*& 不是 const 引用。你的意思是Foo* const&
  • 我的错!我应该更加小心......我有直觉并进行了测试,但我执行了错误的测试。你说的对。我已经删除了我自欺欺人的评论:) +1
  • @JeskoHüttenhain 它是对指针的引用,而不是对引用的指针。对指针的引用可能会被用于重新定位指针并将其替换为分配的对象。
  • 哦,我明白了。我需要更好地阅读 C++ 类型。那么,是的,这实际上是一个很好的例子。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

    创建临时然后在其上调用非常量方法的典型情况是当您要交换它时:

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

    这有时非常有用。

    【讨论】:

    • 为什么我会丢失更改?看看我的问题的标题,这个临时的会和a 一样长,就像它在const Foo& 的情况下一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-12
    • 2011-11-26
    • 1970-01-01
    • 1970-01-01
    • 2011-08-17
    相关资源
    最近更新 更多