为什么在C ++ 11的基于范围的情况下需要按值捕获

时间:2013-04-10 13:45:00

标签: c++ c++11

以下是基于C ++范围的循环的示例,该循环按值

捕获元素
vector<int> v = {1, 3, 5, 7, 9};

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

更多信息 What is the correct way of using C++11's range-based for?


我的问题

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

修改

是的我知道函数允许传递值,但这有明显的好处。我要问的是,在 for循环中是否有使用按值捕获的方法。我现在可以看到,这可以是bools和chars的有用序列。

2 个答案:

答案 0 :(得分:7)

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

  

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

我认为这是你遇到的主要问题。循环不是“为你做”。此 明确要求提供副本。什么比初始化非参考变量更明确?

这实际上只是一个普通的声明。为什么,当它在其他地方有效时,我们会auto本身无效吗?实际上,该声明的初始化由标准定义为:

auto x = *__begin;

其中__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);
}

答案 1 :(得分:2)

我认为部分混淆是由于auto的使用而引起的,虽然不是强制性的,但这种情况很常见。基本上基于范围的for (type var : range)意味着:迭代range中的元素,创建var的局部变量type,使用{{1}中的相应元素初始化}。根据标准规定,此构造具有常规for循环的直接转换:

range

range-init for-range-declaration 可以是任何可以在扩展版本上进行替换和编译的内容。对 for-range-declaration 中的类型没有要求,它甚至可以与 range-init 所拥有的不同:

// 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
  }
}

因为可以使用允许扩展编译的任何类型,所以允许// Convoluted example: void printFloor(std::vector<double> const & v) { for (int i : v) { std::cout << ' ' << i; } } ,其语义与任何其他上下文完全相同,并且行为完全正确与所有其他情况一样。

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