Search code examples
c++transformstdfunctorclamp

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?


Solution

  • 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).