c ++ operator()行为

时间:2012-08-18 10:26:28

标签: c++ c++-standard-library

考虑一下,我使用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;
}

outputs

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

为什么non-zero elements为0?

4 个答案:

答案 0 :(得分: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;

答案 1 :(得分:2)

一般来说 1 标准库提供的算法不是通过引用来接受仿函数,而是按值,因此你不能“从外部”检查它的最终状态,因为仿函数你通过的是未受影响的。

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

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

请注意,由于这里涉及两个副本,通常最好使用副本很便宜的“琐碎”仿函数,如果编译器生成的仿函数不能,则提供复制构造函数非常重要。

然而,正如其他人所指出的,对于您正在执行的任务,有更适合的算法。


  1. Nitpicker的角落:我实际上没有全部检查过,虽然这似乎是我所见过的所有“正常”行为。

答案 2 :(得分:1)

标准库算法通常会复制他们的参数仿函数,因此对其状态的可能性将不可见。

你真的想在这里使用错误的算法。

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

如果要迭代序列并累积某种数据,则应使用... std::accumulate。 :)

该算法采用一个附加参数,即“状态”,并在算法完成后返回此状态。

答案 3 :(得分:0)

正如其他人所说,使用A的不同实例,因为std::for_each没有将仿函数作为参考。如果您slightly modify your code打印A::a的地址

,则可以看到此消息
a:1  @0xbf86902c
a:2  @0xbf86902c
a:2  @0xbf86902c
non-zero elements:0  @0xbf869050