Extending/specializing `template<typename T>zero()` to invocable `T`

Viewed 80

I'm trying to define a function template<typename T> zero() and specialize it to various cases.

zero<T>() should return

  • static T::zero() if that exists.
  • else static_cast<T>(0) if that is defined.

So far so good, as implemented below.

Now I want to extend the template in a sensible way to any T that is implicitly convertible to a type of the form std::function<Y(X...)>, and should return the corresponding zero-returning function [](X...){return zero<Y>();}

What would be the best way to do this?

namespace hidden {

    // tag dispatching
    template<int r>
    struct rank : rank<r - 1> {};

    template<>
    struct rank<0> {};

    template<typename T>
    auto zero(rank<2>) -> decltype(T::zero()) {
        return T::zero();
    }

    template<typename T>
    auto zero(rank<1>) -> decltype(static_cast<T>(0)) {
        return static_cast<T>(0);
    }

    // This is where I need help
    template<typename T>
    auto zero(rank<0>) -> std::enable_if_t</* T is implicitly convertible to std::function<Y(X...)> */,T> {
        using Y = // the type returned when an instance of T is invoked
        return []() {
            return zero<Y>();
        };
    }
}

template<typename T>
auto zero() { return hidden::zero<T>(rank<10>{}); }

Edit:

I've resorted for now to repeating the following with concrete signature for each expected signature:

template<typename T>
auto zero(rank<0>)
    -> std::enable_if_t<std::is_assignable<std::function<double(double)>, T>::value
    , std::function<double(double)>> {
    using Y = double;
        return []() {
            return zero<Y>();
    };
}

but I'm hoping it's possible to replace the copy-pasting with template magic.

1 Answers

In with a deduction guide of std::function:

template <typename T,
          typename F = decltype(std::function{std::declval<T>()}),
          typename Y = typename F::result_type>
F zero(rank<0>) {
    return [](auto&&...) {
        return zero<Y>(rank<10>{});
    };
}

DEMO

In you need to write this trait yourself.


Note, however, that this will not work if T::operator() is overloaded or represents a function template (including generic lambda expressions).

Related