19

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

  1. 后一种形式比前一种有什么优势?为什么我们通常去auto &auto &&如果后者显然是有利的而不是?
  2. 使新的基于范围的循环等效于auto &&于破坏现有代码吗?它会对新代码产生实际影响吗?
  3. 这不会向初学者介绍他们的代码实际上等同于的陷阱auto &&吗?
4

2 回答 2

10

后一种形式比前一种有什么优势?

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

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

如果后者显然是有利的,为什么我们通常会选择auto&而不是?auto&&

你不应该。我能想到的唯一缺点auto&&是它不能保证你对元素所做的更改一定会传播回容器,但这表明设计有问题,我认为不值得防范。

使新的基于范围的循环等同auto &&于破坏现有代码吗?

我看不出它如何破坏现有代码,因为旧语法将继续像今天一样发挥作用。但是,如果您的意思是用新语法替换现有代码,那么如果您要替换的是auto const&表单,它可能会产生影响。请参阅此示例。注意auto const&版本如何调用const成员函数,而其他两个调用非const版本?用简洁版本替换第一个将更改被调用的成员函数。

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

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

这不会向初学者介绍他们的代码实际上等同于的陷阱auto &&吗?

我不确定我理解你的意思,但如果你问初学者是否会在不知道的情况下编写代码,或者理解参考折叠的复杂性,那么是的,他们可能会。但这是您所链接的论文的既定目标之一。论点是,您可以远离一开始就教授那些困难的概念,而是向初学者介绍一种for适用于所有事物的基于范围的循环的单一语法。就这一点而言,我认为语法有优点,但从正确性的角度来看,我不赞成它,因为如果我想要对元素进行只读访问,const我宁愿使用它,然后简洁的语法似乎auto const&不对称。

于 2014-08-24T04:34:02.043 回答
8

后一种形式比前一种有什么优势?如果后者显然是有利的,为什么我们通常会选择auto &而不是?auto &&

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

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

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

这不会向初学者介绍他们的代码实际上等同于的陷阱auto &&吗?

为什么会是一个陷阱?auto &&它的好处是它实际上适用于所有事情。有人可能会争辩说,不必教初学者有关类型推导和引用折叠等的所有细节实际上是一个优点,因为它使语言更容易学习。

于 2014-08-24T04:26:48.577 回答