【问题标题】:Proper style for declaration in range-based for基于范围的声明的正确样式
【发布时间】:2012-04-17 04:43:52
【问题描述】:

This question 提到了 C++11 基于范围的 for 的明显惯用用法。

for (auto& elem: container) {
  // do something with elem
}

不过,我一直怀疑您应该使用哪种参考。输入迭代器可能返回右值。虽然auto 引入的隐式类型可以推导出为const,它会绑定到一个右值,但这似乎不会发生。

使用完美转发的最佳一般做法是什么?

for (auto && elem: container) {
  // do something with elem
}

我认为这里没有缺点,但它看起来有点太可爱了。也许我还没有写足够多的 C++11。

【问题讨论】:

  • 对于无法享受基于范围的可怜的 MSVC10 用户,同样的问题适用于BOOST_FOREACH
  • auto 永远不是const。你需要说auto const &。此外,auto && 不是右值引用,而是更多的“通用引用”。
  • @KerrekSB 很好的信息,const 没有在那里推断出来。我从来没有说过任何关于右值引用的事情;v)
  • 我应该补充一点,我的意思并不是“从不”:当然const int a = 1; auto & ra = a; 按预期工作。但是auto 永远不会变得比它需要的更 const。
  • @KerrekSB 你能澄清一下吗? const 是在函数参数上下文中推导出来的,auto 是根据它定义的,没有例外。这似乎工作正常:int const i = 5; auto &x = i; EDIT 哈哈,好吧 :v) 。好吧,在任何情况下,它都不会猜测应该将左值引用设为 const 以绑定到右值。

标签: c++ for-loop c++11 perfect-forwarding


【解决方案1】:

首先,关于如何使用auto 的一些一般性建议并不特定于 range-for。 auto&& 如果初始值设定项是引用临时值的 xvalue,则可能会出现问题,因为在这种情况下可能不会应用生命周期延长。说得更简单,用代码:

// Pass-through identity function that doesn't construct objects
template<typename T>
T&&
id(T&& t)
{ return std::forward<T>(t); }

// Ok, lifetime extended
// T {} is a prvalue
auto&& i = T {};

T* address = &i;

// Still ok: lifetime of the object referred to by i exceed that of j
// id(whatever) is an xvalue
auto&& j = id(std::move(i));

// No other object is involved or were constructed,
// all those references are bound to the same object
assert( &j == address );

// Oops, temporary expires at semi-colon
// id(whatever) is an xvalue, again
auto&& k = id(T {});

这里发生了一些可疑的事情的重要线索是id 的返回类型为T&amp;&amp;。如果它返回T,那么id(whatever) 将是prvalue,并且返回的临时值将延长其生命周期(但这将涉及构造)。


除此之外,当涉及到 range-for 时,尽管您必须记住 for(auto&amp;&amp; ref: init) { /* body */ } 被指定为大致相当于以下内容(忽略一些在这里无关紧要的细节):

{
    using std::begin;
    using std::end;
    auto&& range = init;
    for(auto b = begin(range), e = end(range); b != e; ++b) {
        auto&& ref = *b;
        /* body */
    }
}

我们现在需要问自己,如果 *b 是一个 xvalue(即迭代器类型有一个 operator* 返回 value_type&amp;&amp;,就像 std::move_iterator&lt;Iterator&gt; 的情况一样)怎么办?然后它必须引用一个ref 寿命更长的对象,因为auto&amp;&amp; ref = *b; 行不涉及临时对象。因此它是安全的。否则,如果*b 是纯右值(即,对于某些对象类型T,迭代器类型有一个operator* 返回T),那么临时的生命周期将在循环体的其余部分得到延长。在所有情况下,您都是安全的(*b 是左值的情况留给读者作为练习)。

我个人大量使用auto&amp;&amp;,有或没有range-for。但我每次都会问自己,初始化器是否是一个 xvalue,如果是,所引用的生命周期是多少。

【讨论】:

  • 好消息。另一种观点……函数可以返回 xvalue 的唯一临时对象是 rvalue 参数。 beginend 不带参数,因此转发它们的结果是安全的。
  • @Potatoswatter 在这种情况下,有趣的是*b(这确实不带参数,更不用说您正确分析的临时参数了)。 beginend 接受参数,但它们只适用于左值。
  • 是的,我指的是beginend 成员,不包括this,因为它是左值。 operator* 接受参数的意义与beginend 基本相同。但是,是的,这才是真正重要的。
猜你喜欢
  • 2018-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-08
  • 2013-04-30
相关资源
最近更新 更多