Search code examples
c++c++14template-meta-programmingconditional-compilation

Can't get a method existence detection mechanism to work


I'm writing templated code which needs to invoke a certain templated operator() of functors it gets - but only if that operator() exists.

I've written the following code:


template <typename>
struct sfinae_true : std::true_type{};

template <class F, typename T, typename... Us>
static auto test_templated_invoke_operator(int) ->
    sfinae_true<decltype(std::declval<F>().template operator()<T>(std::forward(std::declval<Us>())... ))>;

template <class, typename, typename... Us>
static auto test_templated_invoke_operator(long) -> std::false_type;

template <class F, typename T, typename... Us>
struct has_templated_invoke_operator : decltype( test_templated_invoke_operator<F, T, Us...>(int{}) )
{ };

template <bool ActuallyInvoke, typename R, class F, typename T, typename... Ts>
struct invoke_if_possible_inner;

template <class F, typename R, typename T, typename... Ts>
struct invoke_if_possible_inner<false, R, F, T, Ts...>
{
    R operator()(F, Ts&&...) { 
        return R(); 
    }
};

template <class F, typename R, typename T, typename... Ts>
struct invoke_if_possible_inner<true, R, F, T, Ts...>
{
    R operator()(F functor, Ts&&... params)
    {
        return functor.template operator()<T>(std::forward<Ts>(params)...);
    }
};

template <typename T, typename R>
struct invoke_if_possible {
    template <class F, typename... Ts>
    R operator()(F functor, Ts&&... params)
    {
        constexpr bool actually_invoke = has_templated_invoke_operator<F, T, Ts...>::value;
        // static_assert(actually_invoke == true,"Should be able to invoke for now!");
        return invoke_if_possible_inner<actually_invoke, R, F, T, Ts...>{}(functor, std::forward<Ts>(params)...);
    }
};

and here's a small main() function to test it with:

int main() 
{
    invoke_if_possible<int, double> iip;
    auto result = iip(foo{}, 3.0);
    std::cout << "Invoke if possible result is " << result << " (and should be 6.0)" << std::endl;
}

This fails (Coliru) - returning 0.0 instead of 6.0.

My question is: Why doesn't the code invoke the defined operator()? And how can I fix the detection mechanism so that its existence is acknowledged and it is invoked?

Notes:

  • The template parameter T is arbitrary; it has nothing to do with the Us parameters.
  • Yes, it's possible to derive R using the return type for operator() - but only if it exists. So we just provide it.
  • If you enable the static assert - it fails.

Solution

  • Your problem is here:

    std::forward(std::declval<Us>())
    

    std::forward takes a non-deduced template parameter which you're not providing - must be forward<T>(u) - so its deduction unconditionally fails.

    But you don't even need the forward here at all. Just declval<Us>() suffices.