C ++函数模板特化声明和模板参数; none vs.&lt;&gt;与<type> </type>

时间:2014-11-01 12:28:26

标签: 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>()
  • 将模板专业化参数留空,不存在或使用专门类型以及它如何影响结果会产生什么特殊含义? 似乎使用函数参数,这个规范是多余的,编译器无论如何指定它都会推导它(结果是等效的显式规范成为不允许的重新声明)。
  • 我理解,给定一个没有参数的函数,声明中需要显式的专用模板参数来指定定义的函数的实例化 适用于(因为不能以其他方式推断)。但是在这种情况下,意义似乎意味着更多的东西,并且以某种无法预料的方式触发“空”专业化(&lt;&gt;)。怎么样?
  • 为什么在使用print2&lt;&gt;()专门设计print2时必须有一个默认的模板参数?但是没有它?

2 个答案:

答案 0 :(得分:3)

  

离开模板会产生什么特殊含义   specialization参数为空,不存在或与专门化   类型以及它如何影响结果?

如果您确实提供了完整的模板参数列表,那么您只需要明确地为给定的模板参数集专门化函数模板。

如果为模板参数的(可能为空)子集提供参数,那么您明确地将函数模板专门用于必须推导的一组参数。根据[temp.deduct.decl]:

  

declarator-id 引用a的特化的声明中   函数模板,执行模板参数推导   确定声明所涉及的专业化。   具体来说,这是为了显式实例化(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&);

  

似乎有一个函数参数,这个规范是   多余的,无论指定方式如何,编译器都会推断它   (结果是等效的显式规范变为   不允许重新宣布)。

是的,情况确实如此。请考虑如果您无效地指定模板参数会导致错误:

  

但在这种情况下,意义似乎意味着更多的东西   “空”专业化(&lt;&gt;)在某种程度上无法预料地触发   方法。怎么样?

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

如果您明确专门化了这个特定专业化的函数模板,这里print2<>print2<short>,那么就会调用该显式特化。
以何种方式无法预料?

  

为什么在专业化时必须有一个默认的模板参数   print2print2<>()但不是没有它?

因为编译器无法推断出参数。如果你提供一个默认参数,他首先不会 来推断它。

答案 1 :(得分:2)

  

将模板专业化参数留空

会产生什么特殊含义

如果可能,推断缺少参数;空参数列表意味着要推导出所有参数。

  

不存在

这意味着您要声明主要模板,而不是明确的专业化。

  

或使用专门类型

这意味着您要声明该类型的显式特化。

  

以某种无法预料的方式触发“空”特化(&lt;&gt;)。怎么样?

在这两种情况下,都会推导出模板参数。在print1中,主模板声明T与函数参数的类型相同;所以它是从函数参数中推导出来的。在print2中,它使用默认类型short声明,因此使用它。因此,当您认为应该print2<> print2<short>时,您会惊讶地发现print2<> print2<short>

  

为什么在使用print2&lt;&gt;()专门设计print2时必须有一个默认的模板参数?但是没有它?

如果既没有默认参数也没有推导出参数的函数参数,那么就不可能推导出专门化的类型,因此无法使用<>。我不知道你的意思是“没有它”;如果您的意思是“没有<>”,那么您将声明主要模板,而不是专业化,并且参数是通用的。