使用 for_each 修改 std 容器(即使你不应该)

pla*_*nic 5 c++ stl

我正在学习 C++ 的自学课程,学习标准库的工作原理,我想了解这段代码是如何for_each工作的,特别是关于变异对象(与本机数据类型相反)。我意识到你不应该使用for_each这种方式,但这是为了学习。

我原以为这段代码会改变集合中的所有元素,但事实并非如此。

我的问题是: 1. 为什么这段代码不会改变集合?2.如何代码被修改,以便它修改的设置?澄清一下:有没有办法保留for_each并让它操纵集合,或者这是不可能的并且transform必须使用其他一些方法(例如)?

代码

#include <iostream>
#include <algorithm>
#include <set>
using namespace std;

class A {
    int a;
public:
    A(int a) : a(a) {}
    int getA() const { return a; }
    void setA(int a) { this->a = a; }
    bool operator<(const A & b) const { return a<b.a; }
};

struct myprinter { 
    void operator()(const A & a) { cout << a.getA() << ", "; }  
};

struct doubler {
    void operator()(A a) { a.setA(a.getA()*2); }
};

int main() {
    int mynumbers[] = {8, 9, 7, 6, 4, 1};
    set<A> s1(mynumbers, mynumbers+6);
    for_each(s1.begin(), s1.end(), doubler()); //<-- Line in question
    for_each(s1.begin(), s1.end(), myprinter());
    return 0;
}

//Expected output: 2, 8, 12, 14, 16, 18
//Actual output: 1, 4, 6, 7, 8, 9,
Run Code Online (Sandbox Code Playgroud)

到目前为止我尝试过的

  • 起初我认为问题在于 doubler 是按值而不是按引用获取参数,因此它没有将更改保存到集合中。但是当我将签名更改为 时,出现void operator()(A & a)以下错误:

    error: no match for call to '(doubler) (const A&)
    ' __f(*__first);
      ~~~^~~~~~~~~~
    error: binding 'const A' to reference of type 'A&' discards qualifiers
    
    Run Code Online (Sandbox Code Playgroud)

    我推断指出的那条线是从for_each. 我不能使参数成为 const ref,因为我试图a使用该setA()方法更改值,因此它不能是const.

  • 如果我将集合改为向量,那么当我将 doubler 的签名更改为引用时,它成功地将容器中的元素加倍。如果容器是一个集合,为什么它不起作用?

编辑: moooeeeeep 链接到另一个问题,该问题显示了如何编辑 set 的每个元素。这是我的问题的实用解决方案,但我的问题更具理论性 -为什么不能使用 编辑集合for_each,您可以在其中编辑向量和其他 stl 容器?

Som*_*ken 5

因为 astd::set管理其元素的顺序,所以它禁止用户通过它的迭代器更改它的元素。这意味着它begin()end()方法返回 a const_iterator。您只能读取该迭代器指向的元素,而不能修改它(它是 const),而这正是我们doubler()想要做的。

一个解决方案是自己使用std::vectorstd::sort订购它:

#include <iostream>
#include <algorithm>
#include <vector>

class A {
    int a;
public:
    A(int a) : a(a) {}
    int getA() const { return a; }
    void setA(int a) { this->a = a; }
    bool operator<(const A & b) const { return a<b.a; }
};

struct myprinter { 
    void operator()(const A & a) { cout << a.getA() << ", "; }  
};

struct doubler {
    void operator()(A& a) { a.setA(a.getA()*2); } // by reference!
};

int main() {
    int mynumbers[] = {8, 9, 7, 6, 4, 1};
    std::vector<A> s1(mynumbers, mynumbers+6);
    std::sort(s1.begin(), s1.end());
    std::for_each(s1.begin(), s1.end(), doubler());
    std::for_each(s1.begin(), s1.end(), myprinter());
    return 0;
}
Run Code Online (Sandbox Code Playgroud)