Hrant Nurijanyan
Hrant Nurijanyan

Reputation: 929

Create your own functor,C++

I want to create a functor like std::plus<>() but also add std::clamp functionality to it. Let's say it is a function plus_clamp<>(T MIN = numeric_limits<T>::min(), T MAX = ...max()) I started to look what implementation does std::plus<>() have, and tried to repeat it.

So I wrote something like this.

struct plus_clamp {
    template<typename T = void>
    constexpr T operator()(const T& lhs, const T& rhs, T MIN = nl::min(),T MAX = nl::max()) {
        T result = std::plus<>();
        result = std::clamp(result, MIN,MAX);
        return result;
    }
};

But when I try to use that function with std::transform(a.begin(),a.end(),b.begin(),result.begin(), plus_clamp<>())

I get compiler error error: expected '(' for function-style cast or type construction error: expected expression(at template parameter arg) error: expected expression(at function arguments)

I know that I am doing that wrong as I need to pass the template argument and the function arguments explicitly, but then this raises the question how is std::plus<>() implemented so it skips the template and function arguments?

Upvotes: 0

Views: 208

Answers (2)

Alan Birtles
Alan Birtles

Reputation: 36379

There are a number of issues in your code.

  1. There is no way to pass the clamp values into the operator (if using std::transform), you need to pass them as constructor parameters to plus_clamp. This then means that plus_clamp needs to be a template rather than the operator.
  2. Assuming nl is std::numeric_limits it is a template so needs the template type passed to it: std::numeric_limits<T>::min()
  3. std::plus<>() just constructing the type and not calling its operator. It should be std::plus<>{}(lhs, rhs)
  4. When calling std::transform you pass plus_clamp<>(), in your original code plus_clamp isn't a template so you just need plus_clamp().

A fully working example would be:

#include <algorithm>
#include <functional>
#include <limits>

template<typename T = void>
struct plus_clamp {
    constexpr plus_clamp(T MIN = std::numeric_limits<T>::min(),T MAX = std::numeric_limits<T>::max())
    : MIN(MIN), MAX(MAX)
    {
    }

    constexpr T operator()(const T& lhs, const T& rhs) {
        T result = std::plus<>{}(lhs, rhs);
        result = std::clamp(result, MIN,MAX);
        return result;
    }

    T MIN;
    T MAX;
};

int main()
{
    std::vector<int> a,b, result;
    std::transform(a.begin(),a.end(),b.begin(),result.begin(), plus_clamp<int>(10, 100));
}

If you are using c++17 you can use class template argument deduction and just use plus_clamp(10, 100).

Upvotes: 1

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122298

Your plus_clamp is not a template. Hence you need to remove the <>:

std::transform(a.begin(),a.end(),b.begin(),result.begin(), plus_clamp())
                                                                  // ^

std::plus<T> on the other hand is a template. It has an operator():

constexpr T operator()( const T& lhs, const T& rhs ) const;

You can either explitly select the type to add via:

T result = std::plus<T>()(lhs,rhs);
                   //^  T == int in your example
                      //^ create instance 
                        //^ call its operator()

or you use the default std::plus<> which is std::plus<void>. It has a operator() that deduces the type from its parameters:

T result = std::plus<>()(lhs,rhs);

Complete example

Upvotes: 1

Related Questions