函数模板参数推导(class vs 函数模板)

Function template argument deduction (class vs funtion template)

您能帮我理解为什么参数推导适用于 class 模板而不适用于函数模板吗?

如果我没理解错,class 模板定义了一个函数,所以当我调用时,编译器可能会进行隐式转换,但是对于函数模板,没有函数定义目前,隐式转换没有发生。

但我不明白为什么编译器不能创建函数定义然后应用隐式转换?

#include <functional>

template<typename ...ARGS>
class Test1
{
public:
    void add(const std::function<void(ARGS...)>&) {}
};

class Test2
{
public:
    template<typename ...ARGS>
    void add(const std::function<void(ARGS...)>&) {}
};

void func(int) {}

int main()
{
    Test1<int> test1;
    test1.add(func);

    Test2 test2;
    test2.add<int>(func);
}

错误是:

In function 'int main()':

   25:24: error: no matching function for call to 'Test2::add(void (&)(int))'

   25:24: note: candidate is:

   14:10: note: template void Test2::add(const std::function&)

   14:10: note: template argument deduction/substitution failed:

   25:24: note: mismatched types 'const std::function' and 'void(int)'

在第一种情况下,您显式实例化了 class 模板 Test1。这意味着其 add 成员的函数声明是使用签名 add(const std::function<void(int)>&) 生成的。当编译器随后尝试解析 test1.add(func) 时,只有那个候选者。由于 std::function<void(int)> 可以从 void(int) 函数隐式构造,签名匹配,编译器只是实例化成员函数定义,一切都很好。

在第二种情况下,编译器必须执行模板参数 deduction/substitution 以查看它是否可以 "use" add 模板。您可能认为指定 int 会确定模板参数,因此不需要进行推导,但事实并非如此:您的意思可能是部分指定模板参数,例如 here .换句话说,您可能试图用比您明确指定的更多的参数来实例化函数模板,至少编译器不知道您是否这样做。所以它仍然必须尝试匹配 std::function<void(ARGS...)> 的类型(或者更准确地说,std::function<void(int, ...)>void(int),它不能,因为推导不考虑隐式转换。

简而言之:指定显式模板参数不会阻止可变函数模板的模板参数推导。

注意:我不是 100% 确定确切的术语,感谢任何语言律师来纠正我!

编辑:我主要基于我阅读的内容 here

我最初对以下代码片段工作原因的推理是错误的,但由于@NathanOliver 帮助了我(见下文),这里是修改后的解释:在模板参数推导期间,不执行任何类型转换。将函数指针传递给采用 std::function 参数的函数需要这样的转换。为了避免这个问题,你可以这样调用方法

test2.add(std::function<void(int)>(func));

或调整Test2的定义为

class Test2
{
    template<typename ...ARGS>
    void add(void(*)(ARGS...)) {}
}

与原始调用一起工作

test2.add<int>(func);

在这两个示例中,都不需要转换。对 Test1::add 的调用成功了,因为在调用该方法之前已经执行了模板类型推导,因此可以进行转换。

另请注意,当使用一个模板参数声明 Test2 时,也会出现同样的问题,

class Test2
{
    template<typename T>
    void add(const std::function<void(T)>&) {}
}

调用方有以下用例:

test2.add<int>(func); // Conversion ok, function template specified
test2.add(std::function<void(int)>(func)); // Type deduction, no conversion
test2.add(func); // Error, conversion AND type deduction