什么是"简洁的基于范围的循环"?

时间:2014-08-24 04:18:11

标签: c++ c++17

clang已经开始从terse ranged-based for loops实施n3994。通常在引入基于范围的for循环时,我们会看到for (auto & v : vector)形式的代码,以避免不必要的复制。似乎n3994提出for (auto && v : vector)在各方面都是优越的。我有几个问题:

  1. 后者形式对前者有何优势?如果后者明显有利,为什么我们通常会使用auto &代替auto &&
  2. 是否会使新的基于范围的循环等效于auto &&会破坏现有代码?它会对新代码产生实际影响吗?
  3. 这不会给初学者带来他们的代码实际上等同于auto &&吗?

2 个答案:

答案 0 :(得分:10)

  

后者形式对前者有何优势?

for(auto& v : vector)形式中,v的类型被推断为对通过解除引用容器的迭代器类型获得的类型的左值引用。这意味着如果取消引用迭代器的结果是rvalue(想象std::vector<bool>返回表示对单个bool值的引用的代理类型),则代码将无法编译,因为左值引用不能绑定到右值。

当您编写for(auto&& v : vector)时,这是通用引用,这意味着v的类型将在上述情况下推导为右值引用;或者作为通常情况下的左值引用,其中解引用迭代器返回对容器元素的引用。所以它也适用于vector<bool>案例。这就是为什么如果你计划修改你在循环中迭代的元素,那么这个表格应该是首选的。

  

如果后者明显有利,为什么我们通常会使用auto&代替auto&&

你不应该。我能想到的auto&&唯一的缺点是,它并不能保证你对元素所做的改变必然会传播回容器,但这表明设计已经破碎了,我不认为值得保护。

  

是否会使新的基于范围的循环等效于auto &&会破坏现有代码?

我不知道它如何破坏现有代码,因为旧语法将继续像今天一样运行。但是,如果您的意思是使用新语法替换现有代码,那么如果您要替换的代码是auto const&形式,则可能会产生影响。见this example。请注意auto const&版本如何调用const成员函数,而其他两个版本调用非const版本?用简洁版替换第一个将改变被调用的成员函数。

  

它会对新代码产生实际影响吗?

同样,它与今天使用auto&&的旧代码没有什么不同,因此没有任何区别。如果您在不打算修改元素的地方使用它,那么编译器将不再阻止您意外地执行此操作,并且您可能会调用不同的重载,如上例所示。

  

这不会给初学者带来他们的代码实际上等同于auto &&吗?

我不确定我理解你的意思,但是如果你问初学者是否会在不知情的情况下编写代码,或者理解参考文章崩溃的复杂性,那么是的,它是可能他们会。但这是您所关联的论文的既定目标之一。争论的焦点是,您可以在开始时远离教授这些困难的概念,但是为初学者介绍基于范围的for循环的单一语法,该循环适用于所有内容。就这一点而言,我认为语法具有优点,但从const正确性的角度来看,我对它不屑一顾,因为如果我想阅读,我宁愿使用auto const&只访问元素,然后简洁的语法似乎不对称。

答案 1 :(得分:8)

  

后者形式对前者有何优势?如果后者是,我们为什么通常使用auto &代替auto &&   显然有利吗?

如果取消引用迭代器返回代理对象而不是实际引用,则

auto &不起作用,因为您试图将非常量左值引用绑定到临时对象。标准的例子是被称为std::vector<bool>的憎恶;取消引用其迭代器会返回类型为std::vector<bool>::reference的代理对象,该对象表示向量中的单个位。由于大多数迭代器都返回实际引用,因此您不会经常遇到此问题。

  

使新的基于范围的循环等效于auto&amp;&amp;打破现有代码?它会对新代码产生实际影响吗?

不,因为新语法for(elem : range)不会在现有代码中编译。

  

这不会给初学者带来他们的代码实际上等同于auto &&吗?

为什么会出现问题? auto &&的好处是它实际上适用于所有事情。有人可能会说,不必教初学者有关类型演绎和参考折叠等的所有细节实际上是一个加分,因为它使语言更容易学习。