[英]C++ function template specialization declarations and template arguments; none vs. <> vs. <type>
在研究函数模板时,我看到以不同方式声明的特化:
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>()
将模板特化参数保留为空,不存在或具有特殊类型以及它如何影响结果会产生什么特殊含义?
如果您确实提供了完整的模板参数列表,那么您只需要为给定的模板参数集明确地专门化函数模板。
如果为模板参数的(可能为空)子集提供参数,那么您将明确地为一组必须推导出的参数专门化函数模板。 根据[temp.deduct.decl]:
在声明者id引用函数模板的特化的声明中,执行模板参数推导以识别声明引用的特化。 具体来说,这是为了显式实例化(14.7.2),显式特化(14.7.3)和某些朋友声明(14.5.4)。 [...]。 在所有这些情况下,
P
是被认为是潜在匹配的函数模板的类型,A
是来自声明[...]的函数类型。
扣除按照14.8.2.5中的描述完成。如果对于所考虑的功能模板集合,在考虑部分排序后(14.5.6.2),没有匹配或多于一个匹配,则扣除失败,并且在声明情况下,程序是不正确的。
因此,对于没有给出参数的每个参数,或者在根本没有指定列表的情况下,对于特化的每个相应参数及其与主模板的对应参数进行模板参数推导。 该过程在§14.8.2.5中描述,就像我们使用提供的模板参数列表作为模板参数和专业化中参数类型的对象作为函数参数调用主模板一样。
您应该熟悉一个事实,即可以使用指定的某些模板参数调用函数模板,例如
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&);
似乎使用函数参数,此规范是多余的,无论指定方式如何,编译器都会推导出它(结果是等效显式规范成为不允许的重新声明)。
是的,情况确实如此。 请考虑如果您无效地指定模板参数会导致错误:
但在这种情况下,意义似乎意味着更多的东西,而“空”的专业化(<>)是以某种无法预料的方式触发的。 怎么会?
对于调用,首先推导出模板参数。 然后调用那些模板参数的特化。
如果你明确地为这个特殊的专业化设置了一个函数模板,这就是print2<>
,它是print2<short>
,那么就会调用那个显式特化。
以何种方式无法预料?
为什么在使用
print2<>()
专门化print2
时必须有一个默认的模板参数,但不是没有它?
因为编译器无法推断出参数。 如果你提供一个默认的说法,他没有推断它摆在首位。
将模板专门化参数留空是什么特殊含义
如果可能,推断出缺失的论点; 空参数列表表示要推导出所有参数。
不存在的
这意味着您要声明主要模板,而不是明确的专业化。
或者使用专门的类型
这意味着您要声明该类型的显式特化。
“空”特化(<>)以某种无法预料的方式触发。 怎么会?
在这两种情况下,都推导出模板参数。 在print1
,主模板声明T
与函数参数的类型相同; 所以它是从函数参数中推导出来的。 在print2
,它使用默认类型short
,因此使用它。 因此,当您认为print2<>
应该是print2<short>
print2<>
时,您会惊讶地发现: print2<>
是 print2<short>
。
为什么在使用print2 <>()专门化print2时必须有一个默认的模板参数,但不是没有它?
如果既没有默认参数也没有从中推导出参数的函数参数,那么就不可能推导出专门化的类型,因此无法使用<>
。 我不知道你的意思是“没有它”; 如果您的意思是“没有<>
”,那么您将声明主模板,而不是专业化,并且参数是通用的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.