使用空参数包

时间:2015-07-15 20:39:00

标签: c++ templates c++11 template-deduction

考虑这个简单(坏)的功能模板,该网站上存在许多变体:

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

两次试图称之为:

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

我无法调用(a),因为lambda不是std::function<R(Args...)>,因此模板推断失败。直截了当。

但是,(b)也失败了。我怀疑这是因为编译器无法确定我的意思是提供所有类型参数和我只是提供R的原因 - 因此它正在尝试(并且失败)推断{{ 1}}原因与初始调用失败的原因相同。

有没有办法明确指出我提供所有模板参数?为了澄清,我感兴趣如何显式提供模板参数,以便没有模板推论 - 我不是在寻找写Args...的正确方法或者使用lambda调用时,模板推导成功。

2 个答案:

答案 0 :(得分:5)

对你编辑的问题的简短回答是:如果你不能改变call_with()的声明,那么要么使用@CoffeeandCode演示的类型转换,要么使用下面描述的技术创建一个包装器call_with()

问题在于编译器试图从第一个函数参数中推导出模板参数。如果您编写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);
}

使用模板元功能来生成&#34; std :: function类型意味着编译器甚至不能尝试从第一个参数推导出函数类型,它只会使用其他参数。

您仍然需要显式提供返回类型,但是对于其他参数,您现在可以选择是显式指定它们还是将其留给编译器以从给定的参数中推导出它们。

如果你真的想要强制提供所有模板参数而不是推导出来,你也可以通过这种方式将参数包包装在identity的调用中:

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

总之,如果要阻止编译器推导出也作为函数参数出现的函数模板参数类型,请将它们包装在元函数中,例如identity

答案 1 :(得分:0)

您可以事先指定函数类型,如下所示:

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

或者,以更混乱但更紧凑的方式:

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

这是因为在你要求它隐式地将lambda(由编译器定义的未指定函数对象)转换为{{{}之前,编译器不知道要为模板参数推导出什么返回类型和参数。 1}}使用这些模板参数。

实际上,您应该将功能warpper更改为更通用:

std::function

这适用于任何函数或仿函数类型。它也是使用尾随返回类型的一个很好的例子。

Here's a live example