适用于基于范围的声明 [英] Proper style for declaration in range-based for

查看:127
本文介绍了适用于基于范围的声明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

This question mentioned the obvious, idiomatic usage of C++11 range-based for.

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

我一直对你应该使用的引用类型有疑问。输入迭代器可以返回rvalue。虽然 auto 引入的隐式类型可以推导为 const ,它将绑定到右值,

I've been having doubts about the kind of reference you're supposed to use, though. Input iterators may return rvalues. Although the implicit type introduced by auto could be deduced to const which would bind to an rvalue, that doesn't seem to occur.

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

Is the best general practice to use perfect forwarding?

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

我看不到有什么不好,但看起来有点太可爱了。也许我还是没有写足够的C ++ 11。

I see no downside here, but it looks a little too cute. Maybe I still just haven't written enough C++11.

推荐答案

首先,一些建议如何使用 auto ,不特定于range-for。 auto&&& 如果初始化程序是引用临时变量的xvalue,可能会出现问题,因为在这种情况下可能不会应用生命周期扩展。为了更简单,使用代码:

First, some general advice on how to use auto that is not specific to range-for. auto&& can be problematic if the initializer is an xvalue referring to a temporary, since lifetime extension may not be applied in this case. To put it more simply, and with code:

// 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&&< / code>。如果它返回 T ,那么 id(whatever)将是一个prvalue, (但这将涉及一个建设)。

The big clue that there's something shady going on here is that id has return type T&&. If it returned T then id(whatever) would be a prvalue, and the returned temporary would have had its lifetime extended (however that would involve a construction).

你必须记住 for(auto& ref:init){/ * body * /} 被指定为大致相当于以下内容在这里):

With that out of the way, when it comes to range-for though you have to remember that for(auto&& ref: init) { /* body */ } is specified to be roughly equivalent to the following (ignoring some details that don't matter here):

{
    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(即迭代器类型有运算符* 返回 value_type&& ,例如使用 std :: move_iterator< Iterator> )?它必须指向 ref 的对象,因为 auto&& ref = * b; 不涉及临时。因此它是安全的。否则,如果 * b 是prvalue(即迭代器类型具有运算符* 返回对于某些对象类型 T ,T ),则临时的生命周期将扩展到循环体的其余部分。在所有情况下,你是安全的( * b 是一个左值作为练习留给读者的情况)。

We need to ask ourselves now, what if *b is an xvalue (i.e. the iterator type has an operator* returning value_type&&, as is the case e.g. with std::move_iterator<Iterator>)? It must then refer to an object that will outlive ref, since the line auto&& ref = *b; involves no temporary. Hence it's safe. Otherwise, if *b is a prvalue (i.e. the iterator type has an operator* returning T for some object type T), then the lifetime of the temporary gets extended for the rest of the loop body. In all cases you're safe (the case where *b is an lvalue being left as an exercise to the reader).

我个人大量使用 auto&&& ,有或没有range-for。但是我每次询问自己初始化器是否是xvalue,如果是,是什么是被引用的生命周期。

I personally make heavy use of auto&&, with or without range-for. But I do ask myself every time whether the initializer is an xvalue or not, and if it is, what is the lifetime of what is being referred to.

这篇关于适用于基于范围的声明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆