【问题标题】:range based for loop with existing variable具有现有变量的基于范围的 for 循环
【发布时间】:2016-12-01 13:29:40
【问题描述】:

在 C++11 中使用基于范围的 for 循环和现有变量,我希望该变量将填充循环后最后一次迭代的值。但是,当我测试它时,我得到了不同的结果。

例子:

#include <iostream>
#include <vector>
using namespace std;

int main() {
  std::vector<int> v;
  v.push_back(2);
  v.push_back(43);
  v.push_back(99);

  int last = -50;
  for (last : v)
    std::cout << ":" << last << "\n";

  std::cout << last;
  return 0;
}
  1. MSVC 2013 似乎不支持没有类型声明的基于范围的 for 循环
  2. GCC-5.1 要么自动引入一个新变量,要么将其设置回初始值,从而给出

    :2
    :43
    :99
    -50

我猜 MSVC 只是再次成为 MSVC,但是这里的 GCC 呢?为什么最后一行不是last99


鉴于definition by the standard,我预计我在第一句话中描述的行为。

{
  auto && __range = range_expression ; 
  for (auto __begin = begin_expr, __end = end_expr; 
       __begin != __end; ++__begin) { 
    range_declaration = *__begin; 
    loop_statement 
  } 
} 

range_declarationlast 而不是 int last,这应该修改现有变量。

【问题讨论】:

  • 这还能编译吗?我认为last 位必须是一个声明。
  • 啊,这一定是他们先发制人地实现了n3994,它没有进入C++17。
  • @TartanLlama 谢谢,我现在看到选择“range_declaration"”是有充分理由的。我无法推断它实际上必须是一个声明并且认为它可以是任何可分配的表达式。

标签: c++ c++11 for-loop gcc visual-c++


【解决方案1】:

GCC 实施了标准提案n3994,这表明for (elem : range)for (auto&amp;&amp; elem : range) 的语法糖。这并没有进入 C++17,因此该功能已从更新的 GCC 版本中删除。

用于迭代范围的命名变量必须是根据[stmt.ranged] 的声明,因此您的代码不应编译。

【讨论】:

  • 我接受这个答案,因为它是唯一一个声明: 的左侧实际上必须是声明,而不是任意表达式,并且包含对标准的引用。谢谢! :)
【解决方案2】:

您的代码从 gcc 6.1(以及所有 clang 版本)开始无法编译:

main.cpp:12:8: error: range-based for loop requires type for loop variable
  for (last : v)
       ^
       auto &&

看起来以前的版本在这里隐式使用了 auto 。你得到 -50 作为最后一个输出的事实是因为for 为最后一个引入了本地范围,所以在for 结束后,使用了外部范围的最后一个。


我做了一点挖掘,这是故意在 gcc 下:N3994, terse range-for,它很快就会做以下事情:

A range-based for statement of the form
    for ( for-range-identifier : for-range-initializer ) statement
is equivalent to
    for ( auto&& for-range-identifier : for-range-initializer ) statement

然后它没有进入 c++17 并在此处被删除:

https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=229632

【讨论】:

  • 所以 MSVC 不再是 MSVC!
  • 最新的 VS (webcompiler.cloudapp.net) 也不会编译这个。
  • gcc 5.1.0在c++1z模式下编译
  • 5.2.0 和 5.3.0 都编译它
  • @Borgleader OP 没有说它需要 c++1z
【解决方案3】:

你的程序不能用我的 g++ 4.9.2 编译。

使用 clang++ 3.5 编译(带有警告:“具有隐式推导类型的基于范围的 for 循环是 C++1z 扩展 [-Wc++1z-extensions]”)

但是clang++使用不同的last变量

使用以下修改程序

#include <iostream>
#include <vector>
using namespace std;

int main() {
  std::vector<int> v;
  v.push_back(2);
  v.push_back(43);
  v.push_back(99);

  int last = -50;

  std::cout << "extern last pointer: " << long(&last) << '\n';

  for ( last : v)
   {
     std::cout << ": " << last << " ; pointer: " << long(&last) << '\n';
   }

  std::cout << "extern last pointer again: " << long(&last) << '\n';
  std::cout << ": " << last << std::endl;

  return 0;
}

我得到以下输出

extern last pointer: 140721376927168
: 2 ; pointer: 38101008
: 43 ; pointer: 38101012
: 99 ; pointer: 38101016
extern last pointer again: 140721376927168
: -50

【讨论】:

    【解决方案4】:

    根据标准,基于范围的 for 循环产生与以下内容相同的输出:

    {
      auto&& __range = expression;
      for (auto __begin = begin-expression,
                __end = end-expression;
           __begin != __end;
           ++__begin)
      {
        declaration = *__begin;
        statement
      }
    }
    

    如您所见,迭代变量 __begin 是在其自己的范围内定义的。

    为什么最后一行的 last 不是 99?

    您有两个名为last 的变量。一个在您的主要功能范围内,并持有值-50。第二个变量是在基于范围的 for 循环范围内定义的。

    在循环内部,打印last 变量会打印来自同一范围的变量(即来自基于范围的for 循环)。然而,在循环之后,打印 last 将再次打印来自同一范围的变量,即持有 -50 的变量

    【讨论】:

    • declaration 必须是实际声明。而last 不是。
    • @Nicol,我正在回答 Niklas 的第二个问题,即为什么他会获得他在 GCC5.1 中陈述的价值观。将 last 更改为实际的类型声明不会改变这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-14
    • 2020-05-03
    相关资源
    最近更新 更多