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

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

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

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函数。

  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特化版本。

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

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命名空间。

--------------------------------------------------------------------华丽分割线--------------------------------------------------------------------

  上面介绍的内容,涉及到以下的内容: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, , 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

    

C++模板专门化与重载的更多相关文章

  1. c/c++ 模板函数的重载

    模板函数的重载 普通函数可以重载,模板函数也可以重载,但规则复杂 有下面2个函数,名字相同,返回值相同就,参数不同,符合重载. template<typename T> std::stri ...

  2. 聊聊C++模板函数与非模板函数的重载

    前言 函数重载在C++中是一个很重要的特性.之所以有了它才有了操作符重载.iostream.函数子.函数适配器.智能指针等非常有用的东西. 平常在实际的应用中多半要么是模板函数与模板函数重载,或者是非 ...

  3. C++ 函数模板和函数重载同时出现如何调用

    C++ 函数模板和函数重载同时出现如何调用 重点 函数模板不允许自动转换,普通函数可以进行自动类型转换 函数模板可以像普通函数一样被重载 C++编译器优先考虑调用普通函数 如果函数模板可以产生一个更好 ...

  4. c++模板专门化

    #include <iostream> #include<cstring> using namespace std; template <typename T> T ...

  5. [C++] 用Xcode来写C++程序[5] 函数的重载与模板

    用Xcode来写C++程序[5] 函数的重载与模板 此节包括函数重载,隐式函数重载,函数模板,带参数函数模板 函数的重载 #include <iostream> using namespa ...

  6. C++运算符重载 模板友元 new delete ++ = +=

    今天的重载是基于C++ 类模板的,如果需要非类模板的重载的朋友可以把类模板拿掉,同样可以参考,谢谢. 一.类模板中的友元重载 本人喜好类声明与类成员实现分开写的代码风格,如若您喜欢将类成员函数的实现写 ...

  7. C++ Templates (1.5 重载函数模板 Overloading Function Templates)

    返回完整目录 目录 1.5 重载函数模板 Overloading Function Templates 1.5 重载函数模板 Overloading Function Templates 和普通函数一 ...

  8. C++笔记--模板

    一个string模板 简单的定义 template <class C>//模板形式,C是一个类型名字,不一定是某个类的名字 class String{ struct srep; srep ...

  9. C++模板元编程(C++ template metaprogramming)

    实验平台:Win7,VS2013 Community,GCC 4.8.3(在线版) 所谓元编程就是编写直接生成或操纵程序的程序,C++ 模板给 C++ 语言提供了元编程的能力,模板使 C++ 编程变得 ...

随机推荐

  1. 利用多态,实现一般处理程序(ashx)中的AOP(切面编程)

    本文是对工作中的项目进行代码优化(完善登陆验证的AOP切面编程)时,所遇到的各种解决方案思考过程. 项目背景:由ashx+nvelocity构建的简单B/S问卷系统,现需要优化登录验证环节(时隔若干个 ...

  2. 下列哪个为JSP的隐含对象。

    下列哪个为JSP的隐含对象. A.env B.page C.jspinfo D.context 解答:B JSP有九个隐士对象 request对象:保存了很多客户端请求的信息. response对象: ...

  3. 64位系统下,一个32位的程序究竟可以申请到多少内存,4GB还是更多?(一)

    前言: cpu的位是指一次性可处理的数据量是多少,1字节=8位,32位处理器可以一次性处理4个字节的数据量,依次类推.32位操作系统针对的32位的CPU设计.64位操作系统针对的64位的CPU设计.操 ...

  4. ACM/ICPC Moscow Prefinal 2019 趣题记录

    ### Day1: ### **Problem C:** 设$k_i​$为$[A, B]​$中二进制第$i​$位是1的数的个数. 给出$k_0 \cdots k_{63}​$, 求出$[A, B]​$ ...

  5. Python 个人常用汇总

    Python 常用文件操作总结: 导入库路径:sys.path.append('/usr/local/lib/python2.7/site-packages') from random import ...

  6. JDBC批量操作性能提升

    JDBC 当使用INSERT INTO....VALUES()语句批量插入的时候,应该使用JDBC的PreparedStatement的批量操作方法,而不是採用一条一条运行的方法. 比如(来源:htt ...

  7. 网络虚拟化之FlowVisor:网络虚拟层(上)

    概念解释:切片:虚拟网络的一个实例 一. 网络虚拟化(虚拟网络) 人类社会的发展在很大方面得益于自然界,飞机受益于鸟,雷达受益于蝙蝠等等,所以专门有个学科为仿生学就是研究和模仿生物的特殊本质,利用生物 ...

  8. 【BZOJ4260】Codechef REBXOR Trie树+贪心

    [BZOJ4260]Codechef REBXOR Description Input 输入数据的第一行包含一个整数N,表示数组中的元素个数. 第二行包含N个整数A1,A2,…,AN. Output ...

  9. 【Linux】命令学习笔记和总结

    莫名的想学习一下Linux了,因为对这方面的知识储备为0.对于命令行界面始终是零接触零了解,对一个程序员来说这几乎是致命的,所以简单了解一下. 一.教程参考 参考菜鸟教程即可: Linux 教程 | ...

  10. fastJson API

    FastJSON是一个很好的java开源json工具类库,相比其他同类的json类库,它的速度的确是fast,最快!但是文档做得不好,在应用前不得不亲测一些功能.   实际上其他的json处理工具都和 ...