在std :: for_each和std :: transform中正确使用仿函数

时间:2017-02-09 13:44:04

标签: c++ c++11 functor

这可能是一个天真的问题,但无论如何它仍然存在:

假设我有这样一个类:

class func {
    public:
        int operator()(int it) const { return it + 21; }
};

很明显,在上面的类中定义了一个仿函数,如果我这样做:

func obj;
int capture = obj(9);
cout << capture << endl;

结果将是30,显而易见。但是假设我使用std::transform根据上面定义的仿函数使用另一个容器的值来转换一个容器。

vector<int> v, vi;
v.push_back(1);
v.push_back(2);
vi.resize(v.size());

我按照下面的语法,我使用类名直接调用仿函数,并且没有参数传递给仿函数(根据定义它需要):

std::transform(v.begin(), v.end(), vi.begin(), func());

这完美无缺。为什么会这样?尽管没有使用func的实例,并且没有传递参数(显然是第一个容器的元素),为什么这会起作用?

此外,如果我使用func的实例,并使用上面的参数,则会导致编译错误。

 func instance;
 std::transform(v.begin(), v.end(), vi.begin(), instance());

如何在std::transform/std::for_each中正确使用仿函数?为什么调用functor方法的方式有所不同?

另外,从this answer on functors开始,我们有以下代码:

// this is a functor
struct add_x {
  add_x(int x) : x(x) {}
  int operator()(int y) const { return x + y; }

  private:
    int x;
};

std::vector<int> in; // assume this contains a bunch of values)
std::vector<int> out(in.size());
// Pass a functor to std::transform, which calls the functor on every element 
// in the input sequence, and stores the result to the output sequence
std::transform(in.begin(), in.end(), out.begin(), add_x(1));

答案说明add_x(1)在这里扮演一个仿函数(而不是一个实例),在上面给出的例子中,它是如何实现的?

4 个答案:

答案 0 :(得分:4)

std::transform(v.begin(), v.end(), vi.begin(), lambda_function());
//                                             ^^^^^^^^^^^^^^^^^

这不是在调用lambda_function::operator(),它只是创建lambda_function的临时实例。在std::transform内,此对象将以operator()的内容作为参数迭代调用其v

如果您使用C ++ 11的支持初始化,那么发生的事情会更明显:

std::transform(v.begin(), v.end(), vi.begin(), lambda_function{});

或许如果你考虑这个:

lambda_function()(0);
//             ^^    creates instance
//               ^^^ calls operator()

关于你的第二个例子:

std::transform(in.begin(), in.end(), out.begin(), add_x(1));
//                                                ^^^^^^^^

这再次创建add_x的临时实例,但不是调用默认构造函数,而是调用add_x(int x);构造函数。这会初始化函数对象中的某个状态,以便在operator()内调用std::transform时,它会添加给定的数字。

答案 1 :(得分:3)

在此,

std::transform(v.begin(), v.end(), vi.begin(), lambda_function());

lambda_function()默认 - 创建lambda_function的临时实例 lambda_function是一个类,而不是一个对象 lambda_function()是一个对象。

lambda_function instance;
std::transform(v.begin(), v.end(), vi.begin(), instance());

您没有通过该实例,而是试图在没有参数的情况下调用operator()
instance是一个对象,而不是一个类。

要传递实例,请写

lambda_function instance;
std::transform(v.begin(), v.end(), vi.begin(), instance);

答案 2 :(得分:3)

std::transform(InIt first, InIt last, OutIt dest, Fn f)本质上是这样的:

while (first != last) {
    *dest = f(*first);
    first++;
}

所以当你打电话

std::transform(v.begin(), v.end(). vi.begin(), lambda_function());

您正在创建lambda_function类型的临时对象,并将该对象传递给transform。在transform内,该对象会在输入范围的每个元素上被调用,就像在代码int capture = obj(9);中一样。

要使用您已经创建的对象而不是临时对象,只需将其传入:

lambda_function instance;
std::transform(v.begin(), v.end(), vi.begin(), instance);

请注意,此代码在()之后没有instance;那将调用operator()而没有参数,lambda_function没有可以不带参数调用的operator()

答案 3 :(得分:0)

看到区别:

lambda_function obj;
int capture = obj(9);
cout << capture << endl;

VS

int capture = lambda_function()(9);
cout << capture << endl;

和第二个例子:

std::transform(v.begin(), v.end(). vi.begin(), lambda_function());

vs

lambda_function obj;
std::transform(v.begin(), v.end(). vi.begin(), obj);

最新的例子:

std::transform(in.begin(), in.end(), out.begin(), add_x(1));

VS

add_x obj( 1 );
std::transform(in.begin(), in.end(), out.begin(), obj);