0

这是一个基于 C++ 范围的循环的示例,它按值捕获元素

vector<int> v = {1, 3, 5, 7, 9};

for (auto x : v)
    cout << x << ' ';

更多here 使用C ++ 11的基于范围的正确方法是什么?


我的问题

为什么甚至允许按价值捕获?它只是令人困惑且容易出错。如果犯了错误,您会付出很多代价。我无法想象仅通过显式创建副本而不是循环为您执行无法解决的任何问题。有什么具体原因吗?我在这里错过了什么吗?

编辑

是的,我知道函数允许按值传递,但这有一个明显的好处。我要问的是在for 循环中是否有任何按值捕获的用途。我现在可以看到,这可能是有用的 bool 和 char 序列。

4

2 回答 2

7

与我们可以按值传递参数的原因完全相同 - 如果您需要副本,请按值获取。

我无法想象仅通过显式创建副本而不是循环为您执行无法解决的任何问题。

我认为这是您遇到的主要问题。循环不是“为你做”。这明确要求副本。还有什么比初始化一个非引用变量更明确的呢?

这实际上只是一个正常的声明。为什么,当它在其他任何地方都有效时,我们会让它auto自己在这里无效?事实上,这个声明的初始化被标准定义为:

auto x = *__begin;

where__begin是为范围的第一个元素提供迭代器的表达式(在本例中v.begin())。这与 C++ 中的任何其他复制没有什么不同。您认为以下是常见的错误吗?

int x = some_other_int;

或者:

std::string str = some_other_string;

不,当我们想要一个副本时,我们会写这样的声明。

这是一个示例用例:

void modify_argument(X&);
void use(X);

// ...

std::vector<X> v = /* ... */;
for (auto x : v) {
  // We want to modify the copy of x, but not the original:
  modify_argument(x);
  use(x);
}
于 2013-04-10T13:47:03.447 回答
2

我认为部分混淆是由于使用auto,虽然不是强制性的,但很常见。基本上,基于范围的 forfor (type var : range)意味着:迭代元素以range创建var使用. 这个结构在标准规定的常规 for 循环方面具有直接翻译:typerange

// for (for-range-declaration : range-init)
//    statement
{
  auto && __range = range-init;
  for ( auto __begin = begin-expr, __end = end-expr;
            __begin != __end; ++__begin ) {
     for-range-declaration = *__begin;
     statement
  }
}

range-initfor-range-declaration可以是任何可以在扩展版本上替换并编译的东西。对于 for-range-declaration中的类型没有要求,它甚至可以与range-init所持有的不同:

// Convoluted example:
void printFloor(std::vector<double> const & v) {
   for (int i : v) {
       std::cout << ' ' << i;
   }
}

因为可以使用允许扩展编译的任何类型auto,所以它具有与在任何其他上下文中完全相同的语义,并且行为与在所有其他情况下完全相同。

将基于范围的 for 的要求更改为仅允许通过引用进行迭代会不必要地使语言(标准措辞)和实现复杂化(编译器不能只是扩展,因为for-range-declaration不仅仅是一个声明,而是一个限制使用值的形式),实际上限制了在需要值时需要更复杂的用户代码的结构的使用(用户必须手动复制)。请记住,这个结构只是为了简化代码,它不是一个启用功能,没有它就不能用这个结构做任何事情(你总是可以手动生成上面的扩展)。

于 2013-04-10T14:30:48.603 回答