简体   繁体   English

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

[英]Proper use of a functor in std::for_each and std::transform

This may be a naive question but here it goes anyway :这可能是一个幼稚的问题,但无论如何都会这样:

Suppose I have a class like this :假设我有一个这样的课程:

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

As it's clear, a functor is defined in the above class, and if I do this :很明显,在上面的类中定义了一个函子,如果我这样做:

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

The result will be 30, as is obvious.很明显,结果将是 30。 But let's suppose I use STL's std::transform to transform one container using the values of another container according to the functor defined above:但是让我们假设我使用 STL 的std::transform根据上面定义的函子使用另一个容器的值来转换一个容器:

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

I follow the below syntax, where I'm invoking the functor directly using the class-name, and also, no argument is passed to the functor (which it requires according to the definition) :我遵循以下语法,其中我使用类名直接调用函子,并且没有参数传递给函子(根据定义它需要):

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

This works perfectly.这完美地工作。 Why does this happen?为什么会发生这种情况? Despite not using an instance of func and also without passing an argument (which is the element of the first container apparently), why does this work?尽管没有使用func的实例并且也没有传递参数(这显然是第一个容器的元素),为什么这有效?

Also, if I use an instance of func , with an argument as I did above, it causes a compilation error.此外,如果我使用func的实例,并像上面那样使用参数,则会导致编译错误。

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

How to use a functor properly in std::transform/std::for_each ?如何在std::transform/std::for_each正确使用函子? Why is there a difference in the way the functor method is invoked?为什么调用 functor 方法的方式会有所不同?

Also, from this answer on functors , we have the following piece of code :此外,从这个关于 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));

The answer states that add_x(1) acts a functor here (and not an instance), how is it an instance in the above example I gave?答案指出add_x(1)在这里充当函子(而不是实例),在我给出的上述示例中它是如何成为实例的?

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

That isn't invoking lambda_function::operator() , it is just creating a temporary instance of lambda_function .这不是调用lambda_function::operator() ,它只是创建lambda_function的临时实例。 Inside std::transform , this object will have its operator() called iteratively with the contents of v as arguments.std::transform ,该对象将使用v的内容作为参数迭代调用其operator()

It's more obvious what is happening if you use C++11's braced initialization:如果您使用 C++11 的支撑初始化,会发生什么更明显:

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

Or perhaps if you consider this:或者,如果您考虑到这一点:

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

Regarding your second example:关于你的第二个例子:

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

This again creates a temporary instance of add_x , but instead of calling the default constructor, it calls the add_x(int x);这再次创建了一个add_x的临时实例,但它不是调用默认构造函数,而是调用add_x(int x); constructor.构造函数。 This initialises some state in the function object so that when operator() is called inside std::transform , it adds the given number.这会初始化函数对象中的一些状态,以便在std::transform调用operator()时,它会添加给定的数字。

In this,在这方面,

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

lambda_function() default-creates a temporary instance of lambda_function . lambda_function()默认创建一个lambda_function的临时实例。
lambda_function is a class, not an object. lambda_function是一个类,而不是一个对象。
lambda_function() is an object. lambda_function()是一个对象。

In

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

you're not passing the instance, you're trying to call an operator() with no arguments.你没有传递实例,你试图调用一个没有参数的operator()
instance is an object, not a class. instance是一个对象,而不是一个类。

To pass the instance, write要传递实例,请写

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

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

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

So when you call所以当你打电话

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

you're creating a temporary object of type lambda_function and passing that object to transform .您正在创建一个lambda_function类型的临时对象并将该对象传递给transform Inside transform , that object gets called on each element of the input range, just like in your code int capture = obj(9);transform内部,该对象在输入范围的每个元素上被调用,就像在您的代码中一样int capture = obj(9); . .

To use an object that you've already created instead of a temporary, just pass it in:要使用您已经创建的对象而不是临时对象,只需将其传入:

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

Note that this code does not have () after instance ;请注意,此代码在instance之后没有() that would be calling operator() with no arguments, and lambda_function does not have an operator() that can be called with no arguments.将被调用operator()不带任何参数,并lambda_function没有一个operator()可以不带任何参数来调用。

See the difference:看到不同:

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

vs对比

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

and second example:第二个例子:

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

vs对比

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

latest example:最新例子:

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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM