1

In my C++ code I wrote two functors that take to arguments one returns the sum and the other returns the subtract so I could use them as arguments to functions. like this:

template<class T>
class AddValues{
public:
    T operator()(const T &value1, const T &value2) {
            return value1 + value2;
    }
};

template<class T>
class SubstractValues{
public:
    T operator()(const T &value1, const T &value2) {
        return value1 - value2;
    }
};

But now I am looking to write like 6 functors that each one of them takes two arguments and returns true/false wither the first value is <,<=,>,>=,==,!= than the other.

Is there a clearer way to do that rather than defining 6 classes? I'm working with C++11

  • Try using lambdas. They are in fact replacing the functors in c++11. – afkid Jun 14 '20 at 11:36
  • 4
    All of the functors should already be defined. See std::plus, std::minus, std::less, std::greater, .... It's highly recommended to reuse those oppsed to redefining them. – Mikael H Jun 14 '20 at 11:39
  • And lambdas do not replace functors in C++11. Lambdas can make some use cases easier, but the benefit of proper functors is that they are named. A raw lambda is sometimes harder to understand than a properly named functor, since you would have to check the code to see what it actually does. – Mikael H Jun 14 '20 at 11:41
  • @MikaelH Thanks, but I'm looking to write mine, may you help? –  Jun 14 '20 at 11:47
  • @MikaelH and for curiosity may you show how to use std::greater and the other here? –  Jun 14 '20 at 11:52
  • Sure, what is your use case? – Mikael H Jun 14 '20 at 11:57
  • I see the point of use functors if you have something global, a repetitive piece of code that you need to call all the time. But usually, functors are local. Now the idea of naming, well a lambda cam be well named in the same manner. I would argue that for a newbie it is easier to read a lambda than a functor. Besides, you can obviously pass them as function members and so on (you can properly name your template parameters for that). In my opinion, functors bring more complicated boiler code, which in the context of a function it is better to use lambdas/bindings. – afkid Jun 14 '20 at 21:07
  • Now in the OPs use case, I agree it is better to use the STLs ones, but I argue on the generic usefulness of user-defined functors outside of STL. A similar discussion here: https://stackoverflow.com/questions/4686507/lambda-expression-vs-functor-in-c – afkid Jun 14 '20 at 21:10

1 Answers1

0

Note: This post is a refinement of my comments to the original post.

First, you should be aware that the STL already defines a set of functors. See https://en.cppreference.com/w/cpp/header/functional under Comparators for <,<=,>,>=,==,!=, and Arithmetic operations for +,- (which you have redefined). It is good practice to know the STL and how to use it.

How to use them

Functors are objects like any other object and are to be used with value semantics. For a functional look, they define the function operator (operator()) and can be called () on the object directly.

std::less is_less;
bool is_0_less_than_0 = is_less(0,1); // Calls bool operator()(int, int) and evaluates to true

The functors are usually used in combination with the algorithms. For a not so pretty use case to compare two arrays of integers:

std::array<int,4> low_numbers = {1,2,3,4};
std::array<int,4> high_numbers = {5,6,7,8};
std::array<bool,4> is_number_greater;
// compares low_numbers and high_numbers element wise and stores result in is_number_greater.
std::transform(low_numbers.begin(), 
    low_numbers_low.end(), 
    high_numbers.begin(), 
    is_number_greater.begin(),
    std::greater{}); 

How to write your own functors

So you have already (functionality wise) redefined std::plus (as AddValues) and std::minus (as SubtractValues). Note that I say functionality wise, since it is more flexible to only templatize the function operator:

struct AddValues{
    template<class T>
    T operator()(const T &value1, const T &value2) {
             return value1 + value2;
    }
};

And as the member method operator() does not modify any members of AddValues, it should be marked const:

struct AddValues{
    template<class T>
    T operator()(const T &value1, const T &value2) const {
             return value1 + value2;
    }
};

Then you do not need to specify the type when instantiating the object. Compare template class:

AddValues<int> add_values; // templated type has to be explicitly written.
add_values(1,2); //=3

with templated method:

AddValues add_values;
add_values(1,2); //=3, types deduced when calling method.

.

Anyway, you would have to do the same for <,<=,>,>=,==,!=, since you need a wrapper around each operator. The difference would only be that now you return booleans instead of a type.

struct MyLess
{
    template<typename T>
    bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; }
};
Mikael H
  • 1,323
  • 7
  • 12