并行 std::foreach 循环调用非静态 class 成员函数

Parallel std::foreach loop calling a non-static class member function

我想使用 C++17 并行 std::foreach 迭代涉及非静态 class 成员函数的操作。基本上,class 的每个对象都有自己的数据版本和作用于该数据的方法,我想 运行 该方法并行处理一系列值(输入到方法),更新内部数据。类似以下内容:

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

class XYZ{
        std::vector<double> xvec_;
        double m;
  
    public:

        XYZ(std::vector<double> xvec) :xvec_{xvec}, m{0} {}
        
        
        void add_to_m(double x) {
            m += x;            
        }

        void add_up(){
            std::for_each(
                std::execution::par, 
                std::begin(xvec_), 
                std::end(xvec_), 
                add_to_m);           
        }

        void print_m() {
             std::cout << m << std::endl;
        }
};


int main(){
    XYZ xyz1({1,2,3.5,7.8});
    xyz1.add_up();
    xyz1.print_m();
    
    XYZ xyz2({1,2,8});
    xyz2.add_up();
    xyz2.print_m();
}

但这会产生以下错误:

error: invalid use of non-static member function ‘void XYZ::add_to_m(double)’

建议传递给 std::for_each 的函数必须是静态成员。使函数静态化不是一种选择,因为我希望每个对象都对自己的数据版本进行操作。我怎样才能实现这个想法?

编辑:如果您想获得代码的工作非并行版本,请将上面的 add_to_m 实现替换为

void add_up(){   
  for (auto &x : xvec_) add_to_m(x);
}

您需要将对象传递给 std::foreach,其中包含对 this XYZ 的一些引用。否则,它如何知道在哪个对象上调用该方法?一个 lambda 就可以了

std::foreach(std::execution::par, std::begin(xvec_), std::end(xvec_), [&](double x) { add_to_m(x); });

或者,如果您不想重复签名,std::bind_front 也有效

std::foreach(std::execution::par, std::begin(xvec_), std::end(xvec_), std::bind_front(&XYZ::add_to_m, this));

std::bind_front 生成一个像 lambda 一样的仿函数对象,因此 std::bind_front(f, args...)(args2...) 执行 std::invoke(f, args..., args2...)。此外,非静态成员函数指针 f 上的 std::invoke(f, recv, args...) 和指向可以接收该方法的对象的指针 recv 充当 recv->*f(args...).

add_to_m/this->add_to_m 本身并不是您可以用作此类对象的第一个 class“事物”。这只是C++的设计。

请注意,如果实际上 运行 并行,您当前的 add_to_m 会导致一场比赛。