2

考虑一下,我使用 std::for_each 和带有重载 operator() 的对象来积累一些关于向量内容的数据:

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

struct A{
    int a;
    A(): a(0){}

    void operator()(int i) {
        if(i)   a++;
        std::cout << "a:" << a << std::endl;
    }
};

int main(int argc, char *argv[]) {
    //test data
    std::vector<int> vec;
    vec.push_back(1);
    vec.push_back(1);
    vec.push_back(0);

    //accumulator
    A accum;

    std::for_each(vec.begin(), vec.end(), accum);
    std::cout << "non-zero elements:" << accum.a << std::endl;

    return 0;
}

输出

a:1
a:2
a:2
non-zero elements:0

为什么是non-zero elements0?

4

4 回答 4

6

std::for_each()不通过引用获取其第三个参数,因此制作了一个副本accum

如果您添加std::cout语句,A::A()您可以见证这种行为。

请注意,您可以使用以下方法解决此特定问题std::count_if()

std::cout << "non-zero elements: "
          << std::count_if(vec.begin(),
                           vec.end(),
                           [](const int i) { return i != 0; })
          << std::endl;
于 2012-08-18T10:29:10.953 回答
2

通常1标准库提供的算法不会通过引用获取函子,而是按值获取,因此您无法“从外部”检查其最终状态,因为您传递的函子未被触及。

幸运的是,for_each在将仿函数应用于所有元素后返回一个副本,因此,您需要做的是:

accum = std::for_each(vec.begin(), vec.end(), accum);

请注意,由于这里涉及到两个副本,因此通常最好使用副本便宜的“普通”仿函数,并且如果编译器生成的复制构造函数不起作用,则提供一个复制构造函数很重要。

尽管如此,正如其他人指出的那样,对于您正在执行的任务,还有更适合的算法。


  1. Nitpicker 的角落:我实际上并没有检查所有这些,尽管这似乎是我看到的所有我使用的“正常”行为。
于 2012-08-18T10:30:19.127 回答
1

标准库算法通常会复制它们的参数函子,因此对其状态的机会将不可见。

您真的在这里尝试使用错误的算法。

for_each旨在将操作应用于序列中的每个元素,仅此而已。

如果你想遍历序列并积累某种数据,你应该使用std::accumulate...。:)

该算法采用一个附加参数,即“状态”,一旦算法完成,就会返回此状态。

于 2012-08-18T10:35:58.383 回答
0

正如其他人所说,A使用了不同的实例,因为std::for_each不将仿函数作为参考。如果您稍微修改代码以打印以下地址,您可以看到这一点A::a

a:1  @0xbf86902c
a:2  @0xbf86902c
a:2  @0xbf86902c
non-zero elements:0  @0xbf869050
于 2012-08-18T10:40:21.503 回答