使用std :: tie作为循环目标的范围

时间:2014-01-23 05:50:34

标签: c++ for-loop language-lawyer std-pair tie

我想做以下事情:

//std::vector<std::pair<TypeA, TypeB>> someInitializingFunction();
{
  TypeA a;
  TypeB b;

  for (std::tie(a, b) : someInitializingFunction()) {
    // do stuff;
  }
}

但是,这不是有效的代码,因为正如标准所说,基于for循环的范围被定义为等效于:

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

将for-range-declaration定义为:

  

换范围声明:   attribute-specifier-seq_ {opt} decl-specifier-seq声明符

那么阻碍我的是decl-specifier-seq没有被标记为可选项?

因此,似乎我必须依靠旧式的循环来实现这个循环:

std::vector<std::pair<TypeA, TypeB>> myList = someInitializingFunction();

{
  TypeA a;
  TypeB b;

  for (auto it = myList.begin(); it != myList.end(); ++it) {
    std::tie(a, b) = *it;
    // do stuff;
  }
}

但是在语法上看起来似乎有些混乱似乎是一个相当常见的任务,解包函数调用的结果,这在许多其他上下文中都是有效的。

是否有建议将此内容添加到语言中?这是否合情合理?有没有更好的方法来做到这一点,我忽略了?我误读了标准吗?

显然,我可以将我自己的功能放在一起来做这件事,但是使用它也有点麻烦。

2 个答案:

答案 0 :(得分:15)

您仍然可以使用range-for!

//std::vector<std::pair<TypeA, TypeB>> someInitializingFunction();
{
  TypeA a;
  TypeB b;

  for (auto& p : someInitializingFunction()) {
    std::tie(a, b) = p;
    // do stuff;
  }
}

如果您不需要/想要修改const auto& p,请p

更新:使用上述内容,您还可以使用std::move

将元素移至绑定变量
for (auto& p : someInitializingFunction()) {
  std::tie(a, b) = std::move(p);
  // do stuff;
}

您的建议语法可能无法很好地处理。一个人为的例子:

for (std::tie(a, b) : std::move(someInitializingFunction())) {}
// Note: std::move here is superfluous, as it's already an r-value
//    (may also hinder some optimizations). Purely for demonstration purposes.

有了这个,你就无法将元素的值移动到绑定变量,因为来自r值容器的begin()end()等不会产生移动迭代器。 (嗯,是的,你可以将容器调整为返回移动迭代器的东西,但这将是一个全新的故事)

答案 1 :(得分:8)

截至2017年3月21日,结构化绑定是C ++的一部分。

这允许直接执行以下操作:

//std::vector<std::pair<TypeA, TypeB>> someInitializingFunction();
for (auto [a, b] : someInitializingFunction()) {
  // do stuff;
}