简体   繁体   中英

explicit call to variadic function template with empty parameter pack

Consider this simple (bad) function template for which lots of variations exist on this site:

template <typename R, typename... Args>
R call_with(std::function<R(Args...)> f,
            Args... args)
{
    return f(args...);
}

And two attempts at calling it:

call_with([]{});        // (a)
call_with<void>([]{});  // (b)

I cannot call (a) because a lambda is not a std::function<R(Args...)> so template deduction fails. Straightforward.

However, (b) also fails. I suspect this is because the compiler cannot determine that I mean to provide all the type arguments and reasons that I am simply providing R - so it is trying (and failing) to deduce Args... for the same reason that the initial call failed.

Is there a way to explicitly specify that I am providing all the template arguments? To clarify, I am interested only in how to explicitly provide the template arguments so that there is no template deduction - I am not looking for the correct way to write call_with or for a way to make template deduction succeed when called with a lambda.

The short answer to your--edited--question is: if you cannot change the declaration of call_with() , then either use the type casts demonstrated by @CoffeeandCode, or use the technique described below to create a wrapper for call_with() .

The problem is in the fact that the compiler is trying to deduce the template arguments from the first function argument. You can prevent this if you write your code like this :

#include <functional>
#include <iostream>

// identity is a useful meta-function to have around.
// There is no std::identity, alas.
template< typename T>
struct identity
{
    using type = T;
};

template <typename R, typename... Args>
R call_with( typename identity<std::function<R(Args...)>>::type f,
            Args... args)
{
    return f(args...);
}

int main()
{
    call_with<void>([](int){ std::cout << "called" << std::endl; }, 2);
}

Using a template meta-function to "generate" the std::function type means that the compiler cannot even try to deduce the function type from the first argument and it will only use the other arguments.

You would still need to explicitly provide the return type of course, but for the other arguments you can now choose whether to explicitly specify them or leave it up to the compiler to deduce them from the arguments given.

If you really want to enforce that all template arguments are provided and not deduced, you can also wrap the argument pack in an invocation of identity this way:

template <typename R, typename... Args>
R call_with( typename identity<std::function<R(Args...)>>::type f,
            typename identity<Args>::type... args)

In summary, if you want to prevent the compiler from deducing function template argument types that also appear as function parameters, wrap them in a meta function such as identity .

You could specify the function type beforehand, like this:

int main(){
    std::function<void()> f = []{};
    call_with(f);
}

or, in a little more messy but more compact way:

int main(){
    call_with(static_cast<std::function<void()>>([]{}));
}

This is because the compiler doesn't know what return type and arguments to deduce for your template parameters before you ask it to implicitly convert a lambda, which is an unspecified function object defined by the compiler, to a std::function with those template parameters.

Really, you should just change your function warpper to be more generic:

template<typename Functor, typename ... Args>
auto wrapper(Functor &&f, Args &&... args) -> decltype(f(std::forward<Args>(args)...)){
    return f(std::forward<Args>(args)...);
}

This should work for any function or functor type. It's also a really good example of the use of trailing return types.

Here's a live example

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM