10

考虑这个简单的(坏的)函数模板,这个网站上存在很多变体:

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类型参数和原因- 所以它正在尝试(并且失败)推断Args...初始调用失败的相同原因。

有没有办法明确指定我提供所有模板参数?澄清一下,我对如何显式提供模板参数感兴趣,以便没有模板推导 - 我不是在寻找正确的编写方法call_with或在使用 lambda 调用时使模板推导成功的方法。

4

2 回答 2

5

对您的--edited-- 问题的简短回答是:如果您无法更改 的声明call_with(),则使用@CoffeeandCode 演示的类型转换,或使用下面描述的技术为call_with().

问题在于编译器试图从第一个函数参数中推断出模板参数。如果您这样编写代码,则可以防止这种情况发生

#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);
}

使用模板元函数“生成” 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.

于 2015-07-15T22:11:29.317 回答
1

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

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

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

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

这是因为在您要求编译器将 lambda(编译器定义的未指定函数对象)隐式转换为std::function带有这些模板参数的 a 之前,编译器不知道要为模板参数推导出什么返回类型和参数。

真的,你应该改变你的函数变形器更通用:

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

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

这是一个活生生的例子

于 2015-07-16T03:33:35.470 回答