适用于基于范围的声明的样式

Pot*_*ter 12 c++ for-loop perfect-forwarding c++11

这个问题提到了C++ 11基于范围的显而易见的惯用法.

for (auto& elem: container) {
  // do something with elem
}
Run Code Online (Sandbox Code Playgroud)

不过,我一直怀疑你应该使用哪种参考.输入迭代器可以返回rvalues.尽管auto可以推导出引入的隐式类型const将绑定到右值,但这似乎不会发生.

使用完美转发是最好的通用做法吗?

for (auto && elem: container) {
  // do something with elem
}
Run Code Online (Sandbox Code Playgroud)

我觉得这里没有缺点,但看起来有点太可爱了.也许我还没有写好C++ 11.

Luc*_*ton 7

首先,关于如何使用auto它的一些一般性建议并非特定于范围.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 {});
Run Code Online (Sandbox Code Playgroud)

这里有一些阴暗的重要线索是id返回类型T&&.如果它返回T那么id(whatever)将是一个prvalue,并且返回的临时将延长其寿命(但是这将涉及构造).


除此之外,当涉及到范围时 - 虽然你必须记住,for(auto&& 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 */
    }
}
Run Code Online (Sandbox Code Playgroud)

我们现在需要问自己,如果*b是xvalue(即迭代器类型有operator*返回的话value_type&&,如同的情况那样std::move_iterator<Iterator>)?然后,它必须指向一个对象,活得比ref,因为线路auto&& ref = *b;不涉及暂时的.因此它是安全的.否则,如果*b是prvalue(即迭代器类型具有某些对象类型的operator*返回),则临时体的生命周期将延伸到循环体的其余部分.在所有情况下,你都是安全的(左手是左手作为练习留给读者的情况).TT*b

我个人大量使用auto&&,有或没有范围.但是我每次都会问自己,初始化程序是否是xvalue,如果是,是什么,被引用的是什么.