C++ 函数模板特化声明和模板参数;无与 <> 与 <类型>

标签 c++ function templates template-specialization

在研究函数模板时,我看到以不同方式声明的特化:

template<> void f(argtype) {}
template<> void f<>(argtype) {}
template<> void f<argtype>(argtype) {}

...我想知道它们之间的区别。鉴于以下模板函数带参数和不带参数的示例,我有几个问题。

#include <iostream>
#include <typeinfo>

//Function print1 WITH function parameter---------------------------------------------
template<class T>
void print1(T) { std::cout << "Primary template for print1() with type " << typeid(T).name() <<  std::endl; }

template<>
void print1<int>(int) { std::cout << "Specialization for print1<int>(int)" << std::endl; }

//Not allowed, deduced to be the same as print1<int>(int)
/*template<>
void print1<>(int) { std::cout << "Specialization for print1<>(int)" << std::endl; }*/

//Not allowed, deduced to be the same as print1<int>(int)
/*template<>
void print1(int) { std::cout << "Specialization for print1(int)" << std::endl; }*/

//Function print2 WITHOUT function parameter------------------------------------------
/*Not allowed together with print<>(); compiler complains: 
    t2.cpp:29:6: error: template-id 'print2<>' for 'void print2()' does not match any template declaration*/
/*template<class T>
void print2() { std::cout << "Primary template for print2()" << std::endl; }*/

template<class T = short> //Declaration of print2<>() now ok in conjunction with print2<>()
void print2() { std::cout << "Primary template for print2()" << std::endl; }

template<>
void print2<int>() { std::cout << "Specialization for print2<int>()" << std::endl; }

template<>
void print2<>() { std::cout << "Specialization for print2<>()" << std::endl; }

int main() {
    //These three work in the same way, no matter which call method we use, so far so good
    print1(10);
    print1<>(10);
    print1<int>(10);
    print1(true);
    print1<>(true);
    print1<bool>(true);

    print2(); //Triggers print2<>(), a bit unexpectedly, should trigger print2<short>() (primary template)
    print2<>(); //Triggers print2<>(), a bit unexpectedly, should trigger print2<short>() (primary template)
    print2<bool>(); //Triggers print2<bool>() primary template
    print2<short>(); //Triggers print2<>(), should definately trigger primary template for print2()
    print2<int>(); //Triggers print2<int>() specialization
    return 0;
}

输出:

Specialization for print1<int>(int)
Specialization for print1<int>(int)
Specialization for print1<int>(int)
Primary template for print1() with type b
Primary template for print1() with type b
Primary template for print1() with type b
Specialization for print2<>()
Specialization for print2<>()
Primary template for print2()
Specialization for print2<>()
Specialization for print2<int>()
  • 将模板特化参数留空、不存在或使用专业类型会产生什么特殊含义?它如何影响结果? 似乎对于函数参数,这个规范是多余的,无论它是如何指定的,编译器都会推导出它(结果是等效的显式规范成为不允许的重新声明)。
  • 我理解给定一个没有参数的函数,在声明中明确需要专门的模板参数来指定定义函数的实例化 适用于(因为不能以其他方式推导出)。但在这种情况下,其含义似乎暗示了更多内容,并且“空”特化 (<>) 是以某种无法预料的方式触发的。怎么会?
  • 为什么在使用 print2<>() 专门化 print2 时必须有一个默认模板参数,而不是没有它?

最佳答案

What special meaning is derived from leaving the template specialization argument empty, non-existent or with the specialized type and how does it effect the outcome?

如果您确实提供了一个完整的模板参数列表,那么您只是为一组给定的模板参数显式特化了函数模板。

如果您为模板参数的一个(可能为空的)子集提供实参,那么您就是在为一组必须推导的实参明确特化函数模板。根据 [temp.deduct.decl]:

In a declaration whose declarator-id refers to a specialization of a function template, template argument deduction is performed to identify the specialization to which the declaration refers. Specifically, this is done for explicit instantiations (14.7.2), explicit specializations (14.7.3), and certain friend declarations (14.5.4). […]. In all these cases, P is the type of the function template being considered as a potential match and A is […] the function type from the declaration […].
The deduction is done as described in 14.8.2.5.

If, for the set of function templates so considered, there is either no match or more than one match after partial ordering has been considered (14.5.6.2), deduction fails and, in the declaration cases, the program is ill-formed.

因此对于每个没有给出参数的参数,或者在根本没有指定列表的情况下,模板参数推导是针对每个对应的特化参数及其来自主模板的对应参数进行的。该过程在 §14.8.2.5 中进行了描述,其工作方式就像我们调用主模板一样,使用提供的模板参数列表作为模板参数,并将特化中参数类型的对象作为函数参数。
< br/> 你应该熟悉这样一个事实,即可以使用指定的一些模板参数调用函数模板,例如

template <typename A, typename B> void foo(A, B);

foo(7684, 48.);
foo<int>(7684, 48.);
foo<int, double>(7684, 48.);

这对于显式特化是等效的:

template <typename T, typename U>
void foo(T, U) {}

// Both template arguments have to be deduced.
template<> void foo(double, float);

// The *exact* same as above.
// template<> void foo<>(double, float);

// Second template argument has to be deduced by type.
// If we call foo<int>(int(), float()) then the deduced specialization is
// foo<int, float>, thus U=float.
template<> void foo<int>(int, float);

template<> void foo<int, int>(int, int);

这也可以应用于函数模板的重载。在尝试找到与特化对应的主要模板时,选择最专业的模板。

template <typename T, typename U>
void foo(T&, U&) {}

template <typename T, typename U>
void foo(T const&, U&) {}

// Specializes the second overload because it is more specialized.
template <>
void foo(int const&, float&);

请注意,在寻找主模板时,提供的参数(即不被推导)用于检查主模板的结果函数参数与特化的结果函数参数。它们必须相等。

template <typename T, typename U>
void foo(T&, U&) {}

// Error - no matching primary template found.
template <>
void foo<int, int>(float&, int&);

// Dito:
template <>
void foo<int>(int, int&);

It seems that with a function argument, this specification is superfluous and the compiler deduces it no matter how it's specified (with the result that equivalent explicit specifications become unallowed redeclarations).

是的,确实如此。考虑一下,如果您无效地指定了导致错误的模板参数:

But the meaning seems to imply something more in this case and the "empty" specialization (<>) is triggered in a somewhat unforeseen ways. How come?

对于调用,首先推导模板参数。然后调用具有这些模板参数的特化。

如果您为这个特定的特化显式特化了一个函数模板,这里就是 print2<>这是 print2<short> ,那么显式特化就这样被称为。
这在哪些方面是不可预见的?

Why do I have to have a default template parameter when specializing print2 with print2<>() but not without it?

因为编译器无法推导出参数。如果您提供默认参数,他一开始就无法推断出它。

关于C++ 函数模板特化声明和模板参数;无与 <> 与 <类型>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26689307/

相关文章:

参数子句中声明符和抽象声明符之间的 C++11 歧义?

javascript - 如何构建基本 JavaScript 函数

c++ - 为什么编译器可以通过引用传递和值传递来重载函数

php - fatal error : Call to a member function prepare() on null

c++ - 完全特化类模板的函数模板的显式特化

c++ - 无法访问 C++ 中类模板继承第二级上的 protected 类成员

c++ - 使用 QSet 作为 Qt map 容器中的键

c++ - 如何对作为参数传递的数组进行排序?

c++ - 将无向连通图分成两个部分

c++ - 未定义的“函数引用”