简体   繁体   English

用空参数包显式调用variadic函数模板

[英]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. 我不能调用(a)因为lambda不是std::function<R(Args...)>所以模板推导失败。 Straightforward. 直截了当。

However, (b) also fails. 但是, (b)也失败了。 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. 我怀疑这是因为编译器无法确定我的意思是提供所有类型的参数和原因,我只是简单地提供R - 因此它正在尝试(并且失败)推断出Args...原因与初始调用失败的原因相同。

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. 为了澄清,我对如何显式提供模板参数感兴趣,以便没有模板推导 - 我不是在寻找写入call_with的正确方法, call_with是在使用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() . 对你编辑的问题的简短回答是:如果你不能改变call_with()的声明,那么要么使用@CoffeeandCode演示的类型转换,要么使用下面描述的技术为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. 使用模板元函数“生成”std :: function类型意味着编译器甚至不能尝试从第一个参数推导出函数类型,它只会使用其他参数。

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: 如果您确实希望强制提供所有模板参数而不是推导出,那么您也可以通过以下方式将参数包包装在一个identity调用中:

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 . 总之,如果要阻止编译器推导出也作为函数参数出现的函数模板参数类型,请将它们包装在元函数(如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. 这是因为编译器在要求它隐式地将lambda(由编译器定义的未指定函数对象)隐式转换为带有这些模板的std::function之前,编译器不知道要为模板参数推导出什么返回类型和参数。参数。

Really, you should just change your function warpper to be more generic: 实际上,您应该将函数warpper更改为更通用:

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 这是一个实例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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