遍历C ++中的非常量变量

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 abhere 这样的不同变量中的值。

fra*_*sco 51

它不起作用,因为{a,b}您正在复制ab。一个可能的解决方案是使循环变量的指针,采取的地址ab

#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从复制ab,和是类型的const Obj,所以你基本上是试图绑定一个恒定值的可变引用。

可以尝试使用以下方法解决此问题:

for (auto obj : {a, b}) {
    obj.i = 123;
}
Run Code Online (Sandbox Code Playgroud)

但随后很快就会发现的实际值i中的对象ab没有改变。原因是,在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)

输出:

123
123
Run Code Online (Sandbox Code Playgroud)

执行上述操作的另一种方法是使循环使用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在他的回答中所做的那样,但是我有一个习惯,就是尽可能避免使用原始指针,在这种情况下,我只是相信使用引用可以代码更清晰,更清晰。

  • +1。尽管它的“外观”可能稍微复杂一些,但我认为该解决方案比公认的解决方案优越得多,该解决方案不仅更改了指针的语义(如果期望对象,则为“怪异”),并且由于使用了_generically,恶意更好地_在没有“&”号的情况下,“ auto”实际上是_copies_指针,而对用户而言却并不明显。auto关键字是一个肮脏的叛徒。当然,复制指针“几乎无害”,但这只是一个巧合。始终使用实际引用(具有引用/对象语义)更加干净。 (3认同)

Jac*_*ker 5

如果复制ab是所需的行为,则可以使用临时数组而不是初始化列表:

#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构造函数,此方法也有效。