tml*_*len 41 c++ initializer-list
#include <initializer_list>
struct Obj {
int i;
};
Obj a, b;
int main() {
for(Obj& obj : {a, b}) {
obj.i = 123;
}
}
Run Code Online (Sandbox Code Playgroud)
该代码无法编译,因为来自的值initializer_list {a, b}被当作const Obj&,并且不能绑定到非const引用obj。
是否有一种简单的方法可以使类似的构造工作,即遍历像like a和bhere 这样的不同变量中的值。
fra*_*sco 51
它不起作用,因为{a,b}您正在复制a和b。一个可能的解决方案是使循环变量的指针,采取的地址a和b:
#include <initializer_list>
struct Obj {
int i;
};
Obj a, b;
int main() {
for(auto obj : {&a, &b}) {
obj->i = 123;
}
}
Run Code Online (Sandbox Code Playgroud)
现场观看
注意:最好使用auto,因为它可以避免静默的隐式转换
ruo*_*ola 44
为什么不工作的原因是的底层元素std::initializer_list从复制a和b,和是类型的const Obj,所以你基本上是试图绑定一个恒定值的可变引用。
可以尝试使用以下方法解决此问题:
for (auto obj : {a, b}) {
obj.i = 123;
}
Run Code Online (Sandbox Code Playgroud)
但随后很快就会发现的实际值i中的对象a并b没有改变。原因是,在auto此处使用时,循环变量的类型obj将变为Obj,因此您只需循环遍历aand的副本b。
应该解决的实际方法是,您可以使用std::ref(在<functional>标头中定义)使初始化程序列表中的项目为type std::reference_wrapper<Obj>。可以隐式转换为Obj&,因此可以将其保留为循环变量的类型:
#include <functional>
#include <initializer_list>
#include <iostream>
struct Obj {
int i;
};
Obj a, b;
int main()
{
for (Obj& obj : {std::ref(a), std::ref(b)}) {
obj.i = 123;
}
std::cout << a.i << '\n';
std::cout << b.i << '\n';
}
Run Code Online (Sandbox Code Playgroud)
输出:
Run Code Online (Sandbox Code Playgroud)123 123
执行上述操作的另一种方法是使循环使用const auto&和std::reference_wrapper<T>::get。我们可以在此处使用常量引用,因为它们reference_wrapper不会被更改,只是它包装的值可以被更改:
for (const auto& obj : {std::ref(a), std::ref(b)}) {
obj.get().i = 123;
}
Run Code Online (Sandbox Code Playgroud)
但我认为,因为auto在此处强制使用.get(),这非常麻烦,并且前一种方法是解决此问题的首选方法。
在循环中使用原始指针似乎更简单,就像@francesco在他的回答中所做的那样,但是我有一个习惯,就是尽可能避免使用原始指针,在这种情况下,我只是相信使用引用可以代码更清晰,更清晰。
如果复制a和b是所需的行为,则可以使用临时数组而不是初始化列表:
#include <initializer_list>
struct Obj {
int i;
} a, b;
int main() {
typedef Obj obj_arr[];
for(auto &obj : obj_arr{a, b}) {
obj.i = 123;
}
}
Run Code Online (Sandbox Code Playgroud)
即使Obj仅具有move构造函数,此方法也有效。