C++模板专门化与重载

  最近在复习C++有关知识,又从新看<<Effective C++>>,收获颇丰。原来之前看这边书,好多地方都是浅尝辄止。<<Effective C++>>条款25:考虑写出一个不抛出异常的swap函数,涉及到C++模板专门化(Templates Specialization)和函数重载(overloading)问题,而当重载与模板搅合在一块儿时,许多问题都变得“模棱两可”。编程

  首先回顾<<Effective C++>>条款25:考虑写出一个不抛出异常的swap函数,想告诉咱们什么东西。安全

  swap函数为咱们提供了异常安全编程的方法,以及用来做为处理自我赋值一种常见机制,所以实现一个不抛出异常的swap函数,变得相对重要起来。缺省状况下的swap函数的典型实现以下:app

namespace std
{
  template<typename T>
  void swap(T& a, T& b)
  {
       T temp(a);
       a = b;
       b = temp;  
  }        
}

  而后,对于模型数据类型其成员变量是指针,指向一个对象,保存了数据(pointer to implementation手法)。若是copying函数采用了deep copying方法,上面的代码将会很是低效,由于,只须要互换a与b指针便可。问题是,缺省版本swap对类型没法可知这些信息,所以针对上述状况,须要专门化swap函数。ide

  1)若是T是class,能够先让T提供一个swap函数,完成swap功能,而后借由functhon template的全特化,实现专门化的swap函数:函数

class Widge
{
public:
    void swap(Wiget& other)
  {
    using std::swap();
    swap(pImpl, other.pImpl);
  }

private:
    WidetImpl* pImpl;
};

//为程序提供一个特化版本的swap:
namespace std 
{
  
template<>
  void swap<Widegt>(Widget& a, Widget& b)
  { a.swap(b);
  
}
}

  上面的代码很好的与STL容器保持了一致性,由于STL容器也都提供了swap成员函数和std::swap特化版本。this

  2)若是Widget与WidgetImpl不是class,而是class template,特化版本的swap函数,咱们可能想写成这样的形式:spa

namespace std
{
    template<class T>
    void swap<Widegt<T>>(Widget<T>& a, Widget<T>& b)   
    {
        a.swap(b);
    }
}

  然而这个代码却没法经过编译,C++不支持function template的偏特化,咱们须要使用模板函数的重载技术:指针

namespace std
{
    template<class T>
    void swap( Widget<T>& a, Widget<T>& b)   //重载了function templates
    {
        a.swap(b);
    }
}

  问题彷佛已经解决了,嗯,是的,还存在一个问题:用户能够全特化std内的templates,可是不能新的对象(template、function、class)。解决方法是将这些类与swap函数放到新的命名空间中,这边便独立与std命名空间。code

--------------------------------------------------------------------华丽分割线--------------------------------------------------------------------对象

  上面介绍的内容,涉及到如下的内容:1)模板函数;2)重载函数;3)全特化和偏特化。当这些东西交织在一块儿的时候,咱们须要足够的耐心作区分甄别。

  1)模板类、模板函数与重载

  // Example 1: Class vs. function template, and overloading 
  //
  // A class template
  template<typename T> class X { /*...*/ };           // (a) 类模板

  // A function template with two overloads
  template<typename T> void f( T );                  // (b) 函数模板 
  template<typename T> void f( int, T, double );     // (c)  函数模板重载

  (a)、(b)、(c)均没有专门化,这些未被专门化的template又被称为基础基模板。

  2)特化

  template class能够有全特化与偏特化两种, template function仅能全特化。

// Example 1, continued: Specializing templates 
//
// A partial specialization of (a) for pointer types 
template<typename T> class X<T*> { /*...*/ };        

// A full specialization of (a) for int 
template<> class X<int> { /*...*/ };

// A separate base template that overloads (b) and (c) 
// -- NOT a partial specialization of (b), because 
// there's no such thing as a partial specialization 
// of a function template! 
template<class T> void f( T* );             // (d)

// A full specialization of (b) for int 
template<> void f<int>( int );              // (e)

// A plain old function that happens to overload with 
// (b), (c), and (d) -- but not (e), which we'll 
// discuss in a moment 
void f( double );                           // (f)

  当function template与重载搅合在一块儿的时候,就存在匹配哪一个版本函数的问题,匹配规则以下:

  1)首先查找non template function ,若是在这些函数中匹配成功,则匹配结束(first-class citizens)

  2)否认,在base template function 中查找最匹配的函数,并实例化,若是base template function恰巧有提供全特化版本模板函数,则使用全特化版本(sencond-class citizens)

 将以上两个规则运用的例子:

// Example 1, continued: Overload resolution 
// 
bool b; 
int i; 
double d;

f( b );        // calls (b) with T = bool 
f( i, 42, d ); // calls (c) with T = int 
f( &i );       // calls (d) with T = int 
f( i );        // calls (e) 
f( d );        // calls (f)

  最后一个问题:如何判断哪一个base template function被specialization,再看下面的例子:

// Example 2: Explicit specialization 
// 
template<class T> // (a) a base template 
void f( T );

template<class T> // (b) a second base template, overloads (a) 
void f( T* );     //     (function templates can't be partially 
                  //     specialized; they overload instead)

template<>        // (c) explicit specialization of (b) 
void f<>(int*);

// ...

int *p; 
f( p );           // calls (c)

  c是b是全特化,f(p)将会调用,符合人们的通常想法,可是,若是置换b与c的顺序,结果就不那么同样了:

// Example 3: The Dimov/Abrahams Example 
// 
template<class T> // (a) same old base template as before 
void f( T );

template<>        // (c) explicit specialization, this time of (a)
void f<>(int*);

template<class T> // (b) a second base template, overloads (a) 
void f( T* );

// ...

int *p; 
f( p );           // calls (b)! overload resolution ignores 
                  // specializations and operates on the base 
                  // function templates only

  这个时候,c将是a的全特化(编译器没看到后面的b的定义)。按照配对规则,首先查找base template function最适合匹配的,b正好最为匹配,而且没有全特化版本,所以将会调用b。

  重要准则:

  1)若是咱们但愿客户化base template function,直接利用传统的函数形式,若是使用重载形式,那么请不要提供全特化版本。

  2)若是正在编写一个base template function,不要提供特化和重载版本,将客户化定制功能下放给用户。实现方法是,在class template 同static 函数接口:

// Example 4: Illustrating Moral #2 
// 
template<class T> 
struct FImpl;

template<class T> 
void f( T t ) { FImpl<T>::f( t ); } // users, don't touch this!

template<class T> 
struct FImpl 
{ 
  static void f( T t ); // users, go ahead and specialize this 
};

  准则2的动机就是利用class template 特化与偏特化功能实现function 特化与偏特化功能。

 

参考文献:

<<Effective C++>>, Scott Meyers

<<Why Not Specialize Function Templates?>>, C/C++ Users Journal, 19(7), July 2001

相关文章
相关标签/搜索