Transparent Operator Functors

The transparent operator functors proposal is there as a way to have generalised functors that are located in <functional>. I personally believe the proposal itself has a very good example that would help illustrate the need for it. However I’ll go ahead and try to explain it as well.

Suppose you have a function, a very basic function mind you:

template<typename T, typename U>
auto less_than(T&& t, U&& u) -> decltype(std::forward<T>(t) < std::forward<U>(u)) {
    return std::forward<T>(t) < std::forward<U>(u);
}

However you want to use this generalised function in the <algorithm> header. You have two options, to make it a struct functor:

struct MyLessThanFunctor {
    template<typename T, typename U>
    auto operator()(T&& t, U&& u) -> decltype(std::forward<T>(t) < std::forward<U>(u)){
        return std::forward<T>(t) < std::forward<U>(u);
    }
};

Or in C++14, to make a polymorphic lambda:

[](auto&& t, auto&& u) -> decltype(auto) { 
    return std::forward<decltype(t)>(t) < std::forward<decltype(u)>(u); 
}

Both are very verbose when used in an algorithm like so:

int main() {
    std::vector<int> v = {112,12,1281271,1919101,29181,412,1 };
    std::sort(std::begin(v), std::end(v), MyLessThanFunctor()); // one
    std::sort(std::begin(v), std::end(v), [](auto&& t, auto&& u) -> decltype(auto) { 
        return std::forward<decltype(t)>(t) < std::forward<decltype(u)>(u); 
    });
}

This proposal aims to make it more compact and generalised by doing this instead:

std::sort(std::begin(v), std::end(v), std::less<>());

This gives you perfect forwarding and solves issues with truncation or problems that arise from changing the container but not the underlying type appointed by the container as mentioned by the paper.

Suppose you have a non-generalised functor:

struct Functor {
    bool operator()(uint32_t a, uint32_t b) {
        return a < b;
    }
};

And you use it with your std::vector<uint32_t> and it works all fine, but you forget about your functor not being generalised and use it with your std::vector<uint64_t>. Can you see the issue that has arisen? The elements will be truncated before being compared which is probably not what the user wanted. Generalised functors solve this issue for you before they arise.

Leave a Comment