【问题标题】:Can I use structured bindings and a for-each loop to iterate through a few “packed-together” values?我可以使用结构化绑定和 for-each 循环来遍历几个“打包在一起”的值吗?
【发布时间】:2021-06-22 13:21:53
【问题描述】:

我经常使用初始化列表和 for-each 循环来遍历少量的 ad-hoc 值,如下所示:

for (auto x : {1, 2, 6, 24, 120}) {
  do_something(x);
}

我最近尝试编写类似的东西,但使用结构化绑定和打包在一起的值:

for (auto[dx, dy] : {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}) {
  try_to_move(dx, dy); // nope! won’t compile
}

不幸的是,这不能编译。 Clang 告诉我:

error: cannot use type ‘void’ as a range

事实上,即使像 auto mylist = {{1, 2}, {3, 4}}; 这样的东西也不会编译。

这给我留下了两个问题:

  1. 是否有替代语法以简洁易读的方式完成我想要的?

  2. 为什么auto mylist 的类型不被解析为initializer_list<initializer_list<int>>?这不是很好吗?

【问题讨论】:

  • 平台?编译器?
  • @SeverinPappadeux 我看不出这对这个问题有多重要。这是一个 C++ 语言问题,与平台和编译器无关。
  • @SeverinPappadeux 这些是不同的东西。您指定的类型很好。 OP 询问是否可以仅从 { {}, ... } 推断出,这实际上是不可能的。
  • 无关:你可以给它一些指导,它应该可以工作:{std::pair{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
  • @PresleyGraham:“初始化器列表”之所以如此命名,是因为您应该使用它们来初始化一个对象。您并没有真正使用它们来做到这一点,因此您有点滥用该功能。这就是为什么它有奇怪的极端情况,它不能按照你想要的方式工作。

标签: c++ c++17


【解决方案1】:

为什么 auto mylist 的类型不被解析为initializer_list<initializer_list<int>>?那不是很好吗?

原因是根本没有人提出它。

方便的语法auto x = {1, 2, 6, 24, 120}; 来自提案N3912,该提案被C++17 采用(另见N3922)。

扣费流程在[dcl.type.auto.deduct]/4中概述:

如果占位符是 auto 类型说明符,则使用模板参数推导规则确定推导的类型 T' 替换 T。通过将出现的auto 替换为新发明的类型模板参数U,或者如果初始化是copy-list-initialization,则使用std​::​initializer_­list<U> 从T 中获取P。使用template argument deduction from a function call的规则推导出U的值,其中P为函数模板形参类型,对应实参为e。如果扣除失败,则声明格式错误。

由于假设函数调用 f({1, 2}) 中的类型推导会失败,因此嵌套的 braced-init-list 推导 auto x = { {1, 2}, {3, 4} }; 也是不可能的。

我想同样的技巧可以应用于函数调用的推导,这将使嵌套的initializer_list 推导成为可能。

欢迎提出后续建议。

是否有替代语法以简洁易读的方式完成我想要的?

你可以定义一个好的旧多维数组:

int lst[][2] = { {1, 2}, {3, 4}, {5, 6}, {7, 8} };
for (auto [x, y] : lst) {
    . . .
}

或者按照 cmets 中的建议,给第一对一个类型来帮助推断:

for (auto [x, y] : { std::pair{1, 2}, {3, 4}, {5, 6}, {7, 8} }) {
    . . .
}

【讨论】:

  • 请注意,initializer_list<initializer_list<int>>能够按要求进行分解,因为它没有也不能具有用于结构化绑定的适当接口
  • 好点。我想这涵盖了问题的“那不是很好吗?”部分。更多细节在这里12
【解决方案2】:

你可以这样使用它。您只需首先将您的临时列表初始化为一个变量。

vector<pair<int, int> > p = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
for (auto x : p) {
    try_to_move(x);
}

然后在函数中像这样访问参数x

<return-type> try_to_move(pair<int, int> x){
    int dx = x.first;
    int dy = x.second;
    // TO-DO
}

【讨论】:

  • std::vector 确实分配,与 initializer_list 相反。
猜你喜欢
  • 2017-06-30
  • 2013-11-04
  • 2021-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-30
  • 2013-10-15
  • 2023-03-19
相关资源
最近更新 更多