【问题标题】:Why passing struct function to for_each loop does not change the struct attributes为什么将结构函数传递给 for_each 循环不会改变结构属性
【发布时间】:2021-02-12 04:53:34
【问题描述】:

我是 C++ 新手,我正在学习 C++20。我正在尝试一个结构函数,它是将一个函数包装在一个结构中,而我们可以在这个结构中声明本地属性。

问题是当我将此结构函数传递给for_each 函数时,它不起作用。

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

using namespace std;

struct accumulateAmount{
    int total_amount;
    accumulateAmount() { total_amount = 100 ;} //constructor
    void operator()(int num){
        total_amount += num;
    }
};


int main(){ 
    vector<int> nums{1,2,3,4,5};
    accumulateAmount acctor;
    for_each(nums.begin(), nums.end(), acctor);
    cout << acctor.total_amount << endl;
    return 0;
    
}

输出为100。它没有实现累加器功能。

如果我将循环从 for_each 更改为普通 for 循环,如下所示:

for (int i = 0; i < nums.size(); i++){
      acctor(nums[i]);
}
 

有效。

所以我想知道是不是因为 'for_each' 包含并行计算,因此对于向量中的每个 int,我们对它们使用独立的函数?

【问题讨论】:

  • 您不会将结果分配给任何东西。无论如何,Homespun 函子不是要走的路,你需要一个 lambda。

标签: c++


【解决方案1】:

std::for_each 按值获取函数。所以你的函数被复制了,std::for_each 调用了copy。这就是您的acctor 不会被修改的原因。

您可以使用std::ref 强制通过引用传递:

for_each(nums.begin(), nums.end(), std::ref(acctor));

或者,也许更惯用,您可以捕获std::for_each 的返回值:

auto const result = for_each(nums.begin(), nums.end(), accumulateAmount());
std::cout << result.total_amount << "\n";

这段代码的好处是您甚至不需要为acctor 引入名称:您可以传递一个临时值并即时创建函数对象。这很好,因为这意味着您可以制作所有本地对象const

也就是说,带有可变函数对象的std::for_each 绝对是不是惯用的C++。找到合适的算法并不总是显而易见的,但总是值得的。在这种情况下,您将使用std::reduce

auto const result = std::reduce(nums.begin(), nums.end(), 100);
std::cout << result << "\n";

【讨论】:

    【解决方案2】:

    最快的解决方法:

    acctor = std::for_each(nums.begin(), nums.end(), accumulateAmount());
    

    但与其从头开始旋转您自己的函子,不如使用 C++ 的 lambda (C++11+)。

    #include <algorithm>
    #include <iostream>
    #include <string>
    #include <vector>
    
    int main(){
        std::vector<int> nums{1,2,3,4,5};
        int val = 100;
        std::for_each(nums.begin(), nums.end(), [&val](int n) { return val += n; });
        std::cout << val << '\n';
    
        return 0;
    }
    

    关于 lambda 的一些快速说明:

    • [] 是您可以在 lambda 范围之外“捕获”变量的地方,这些变量不应作为参数传递。在这种情况下,我通过引用捕获val。您不需要捕获任何内容,但[] 是必需的。
    • () 参数列表,非常直观。
    • {} 函数体,也很简单。

    正如在对另一个答案的评论中指出的那样,使用std::accumulatestd::reduce 可以更简单地解决这个特定示例(显示在另一个答案中(这个答案对我来说是新的,非常酷))。

    #include <iostream>
    #include <numeric>
    #include <string>
    #include <vector>
    
    int main() {
      std::vector<int> nums{1, 2, 3, 4, 5};
      std::cout << std::accumulate(nums.begin(), nums.end(), 100) << '\n';
    
      return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2015-02-12
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      • 2018-03-25
      • 1970-01-01
      相关资源
      最近更新 更多