隐式模板参数无效,但无论如何都要编译

时间:2015-12-12 17:40:31

标签: c++ templates c++11 variadic-templates implicit-conversion

我有以下课程:

class FunctionCallback
{
public:
    static CallbackHandle Create(const std::function<void(void)> &function);
    template<typename T,typename... TARGS>
        static CallbackHandle Create(const std::function<T(TARGS...)> &function);
};

然后我打电话给#39;创建&#39;像这样:

FunctionCallback::Create<void,float>([](float f) -> void {}); // Whether I use a lambda-function or a function pointer makes no difference

尽管这应该是正确的(?),但是visual studio强调读取行与消息:

 Error: no instance of overloaded function "FunctionCallback::Create" matches the argument list
    argument types are: (lambda []void (float f)->void)

然而,Visual Studio中的程序编译很好,没有任何警告或错误。 g ++ - 5无法用类似的消息完全编译它。

将其更改为:

FunctionCallback::Create<void,float>(std::function<void(float)>([](float f) -> void {}));

它不再显示消息,并在Windows和Linux上编译。

为什么不能正确推断出类型,除非我明确指出它?

1 个答案:

答案 0 :(得分:0)

你几乎从不想推断出类型橡皮擦的类型,std::function就是一个例子。除了少数例外,模板类型推导试图推断出参数的确切类型。对于std::function<T> - 类型参数,这意味着如果相应的参数也是T,编译器可以推导出std::function。 lambda表达式的类型不是std::function

即使您明确指定了类型模板参数,在相应的类型被参数替换后,推论仍会继续。对于参数包,编译器仍会尝试推导其余参数,因此对于FunctionCallback::Create<void,float>,编译器最终会得到:

template <typename... TARGS>
static CallbackHandle Create(std::function<void(float, TARGS...)> function);

并且除了std::function<void(float, TARGS...)>之外,其他任何东西都不匹配(或者是从这种类型派生的东西);否则TARGS的扣除失败。

如果您的目标是始终明确指定参数包的类型,则可以将该包(或整个参数)放在非推导的上下文中:

template <typename T> struct identity { using type = T; };
template <typename T> using identity_t = typename identity<T>::type;

struct FunctionCallback
{
    template <typename T, typename... TARGS>
    static void Create(identity_t<std::function<T(TARGS...)>> function);
};

FunctionCallback::Create<void, float>([](float f) -> void {});

DEMO

但是,在这种情况下,您必须付出类型擦除的代价。相反,您可以考虑接受任何函数对象类型:,让编译器推导出参数的确切类型:

struct FunctionCallback
{
    template <typename F>
    static void Create(F function){}
};

FunctionCallback::Create([](float f) -> void {});

DEMO 2