0

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. 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:

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?

Also, if I use an instance of func, with an argument as I did above, it causes a compilation error.

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

How to use a functor properly in std::transform/std::for_each? Why is there a difference in the way the functor method is invoked?

Also, from this answer on functors, we have the following piece of code :

// 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?

Jarvis
  • 8,494
  • 3
  • 27
  • 58
  • 1
    This is an instance: `lambda_function()`. – juanchopanza Feb 09 '17 at 13:46
  • If its an instance, how is it working as a functor, without an argument ? Shouldn't it throw a compilation-error ? – Jarvis Feb 09 '17 at 13:48
  • Why are you calling things "lambda_function" and "capture" when your code has nothing to do with lambda functions and captures? – Jonathan Wakely Feb 09 '17 at 13:56
  • The answer you linked seems to use the term "functor" for both functor classes and their instances, which can be a bit confusing for a beginner. (Non-beginners can often tell from the context whether it refers to a class or an instance of a class.) `add_x(1)` is a functor object which is an instance of the `add_x` functor class. – molbdnilo Feb 09 '17 at 13:59

4 Answers4

4
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. Inside std::transform, this object will have its operator() called iteratively with the contents of v as arguments.

It's more obvious what is happening if you use C++11's braced initialization:

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); constructor. This initialises some state in the function object so that when operator() is called inside std::transform, it adds the given number.

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
3

In this,

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

lambda_function() default-creates a temporary instance of lambda_function.
lambda_function is a class, not an object.
lambda_function() is an object.

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.
instance is an object, not a class.

To pass the instance, write

lambda_function instance;
std::transform(v.begin(), v.end(), vi.begin(), instance);
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • @Jarvis See if my comment under the question makes anything clearer. – molbdnilo Feb 09 '17 at 14:02
  • Actually not completely, how does `add_x(1)` adds 1 to every element of the first container, whereas the definition of `add_x(int y)` adds `y` to the class-member `x` ? Also, is `add_x(1)` the constructor calling or the functor calling ? – Jarvis Feb 09 '17 at 14:06
3

std::transform(InIt first, InIt last, OutIt dest, Fn f) in essence does this:

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. Inside transform, that object gets called on each element of the input range, just like in your code 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; that would be calling operator() with no arguments, and lambda_function does not have an operator() that can be called with no arguments.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
0

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);
Slava
  • 43,454
  • 1
  • 47
  • 90