This question提到了C ++ 11基于范围的显而易见的惯用法。
for (auto& elem: container) {
// do something with elem
}
尽管如此,我一直怀疑你应该使用哪种参考。输入迭代器可以返回rvalues。虽然auto
引入的隐式类型可以推导到绑定到右值的const
,但这似乎不会发生。
使用完美转发是最佳通用做法吗?
for (auto && elem: container) {
// do something with elem
}
我认为这里没有任何缺点,但它看起来有点太可爱了。也许我还没有写好C ++ 11。
答案 0 :(得分:7)
首先,关于如何使用auto
的一些一般性建议,这些建议并非特定于range-for。如果初始值设定项是引用临时值的xvalue,则auto&&
可能会出现问题,因为在这种情况下可能不会应用生命周期扩展。更简单地说,并使用代码:
// 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&&
。如果它返回T
,则id(whatever)
将是一个prvalue,并且返回的临时值将延长其生命周期(但这将涉及构造)。
除此之外,当涉及到范围时 - 虽然您必须记住for(auto&& ref: init) { /* body */ }
被指定为大致相当于以下(忽略一些在这里无关紧要的细节):< / p>
{
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&&
怎么办,例如std::move_iterator<Iterator>
就是这种情况)?然后它必须引用将比ref
更长的对象,因为行auto&& ref = *b;
不涉及临时。因此它是安全的。否则,如果*b
是prvalue(即迭代器类型的某个对象类型operator*
的{{1}}返回T
),则临时的生命周期将延长循环体的其余部分。在所有情况下,您都是安全的(T
是左值作为练习给读者留下的情况。)
我个人大量使用*b
,有或没有范围。但是我每次都会问自己,初始化程序是否是xvalue,如果是,是什么,被引用的是什么。