分享
 
 
 

翻译:Effective C++, 3rd Edition, Item 25: 考虑支持不抛异常的 swap(下)

王朝c/c++·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

(接上篇)

因此该怎么做呢?我们还是需要一个方法,既使其他人能调用 swap,又能让我们得到更高效的模板特化版本。答案很简单。我们还是声明一个非成员 swap 来调用成员 swap,只是不再将那个非成员函数声明为 std::swap 的特化或重载。例如,如果我们的 Widget 相关机能都在 namespace WidgetStuff 中,它看起来就像这个样子:

namespace WidgetStuff {

... // templatized WidgetImpl, etc.

template<typename T> // as before, including the swap

class Widget { ... }; // member function

...

template<typename T> // non-member swap function;

void swap(Widget<T>& a, // not part of the std namespace

Widget<T>& b)

{

a.swap(b);

}

}

现在,如果某处有代码使用两个 Widget 对象调用 swap,C++ 的名字查找规则(以参数依赖查找(argument-dependent lookup)或 Koenig 查找(Koenig lookup)著称的特定规则)将找到 WidgetStuff 中的 Widget 专用版本。而这正是我们想要的。

这个方法无论对于类模板还是对于类都能很好地工作,所以看起来我们应该总是使用它。不幸的是,此处还是存在一个需要为类特化 std::swap 的动机(过一会儿我会讲到它),所以如果你希望你的 swap 的类专用版本在尽可能多的上下文中都能够调用(而你也确实这样做了),你就既要在你的类所在的 namespace 中写一个非成员版本,又要提供一个 std::swap 的特化版本。

顺便提一下,如果你不使用 namespaces,上面所讲的一切依然适用(也就是说,你还是需要一个非成员 swap 来调用成员 swap),但是你为什么要把你的类,模板,函数,枚举(此处作者连用了两个词(enum, enumerant),不知有何区别——译者注)和 typedef 名字都堆在全局 namespace 中呢?你觉得合适吗?

迄今为止我所写的每一件事情都适用于 swap 的作成者,但是有一种状况值得从客户的观点来看一看。假设你写了一个函数模板来交换两个对象的值:

template<typename T>

void doSomething(T& obj1, T& obj2)

{

...

swap(obj1, obj2);

...

}

哪一个 swap 应该被调用呢?std 中的通用版本,你知道它必定存在;std 中的通用版本的特化,可能存在,也可能不存在;T 专用版本,可能存在,也可能不存在,可能在一个 namespace 中,也可能不在一个 namespace 中(但是肯定不在 std 中)。究竟该调用哪一个呢?如果 T 专用版本存在,你希望调用它,如果它不存在,就回过头来调用 std 中的通用版本。如下这样就可以符合你的希望:

template<typename T>

void doSomething(T& obj1, T& obj2)

{

using std::swap; // make std::swap available in this function

...

swap(obj1, obj2); // call the best swap for objects of type T

...

}

当编译器看到这个 swap 调用,他会寻找正确的 swap 版本来调用。C++ 的名字查找规则确保能找到在全局 namespace 或者与 T 同一个 namespace 中的 T 专用的 swap。(例如,如果 T 是 namespace WidgetStuff 中的 Widget,编译器会利用参数依赖查找(argument-dependent lookup)找到 WidgetStuff 中的 swap。)如果 T 专用 swap 不存在,编译器将使用 std 中的 swap,这归功于此函数中的 using 声明使 std::swap 在此可见。尽管如此,相对于通用模板,编译器还是更喜欢 T 专用的 std::swap 的特化,所以如果 std::swap 对 T 进行了特化,则特化的版本会被使用。

得到正确的 swap 调用是如此地容易。你需要小心的一件事是不要对调用加以限定,因为这将影响 C++ 确定该调用的函数,如果你这样写对 swap 的调用,

std::swap(obj1, obj2); // the wrong way to call swap

这将强制编译器只考虑 std 中的 swap(包括任何模板特化),因此排除了定义在别处的更为适用的 T 专用版本被调用的可能性。唉,一些被误导的程序员就是用这种方法限定对 swap 的调用,这也就是为你的类完全地特化 std::swap 很重要的原因:它使得以这种被误导的方式写出的代码可以用到类型专用的 swap 实现。(这样的代码还存在于现在的一些标准库实现中,所以它将有利于你帮助这样的代码尽可能高效地工作。)

到此为止,我们讨论了缺省的 swap,成员 swaps,非成员 swaps,std::swap 的特化版本,以及对 swap 的调用,所以让我们总结一下目前的状况。

首先,如果 swap 的缺省实现为你的类或类模板提供了可接受的性能,你不需要做任何事。任何试图交换你的类型的对象的人都会得到缺省版本的支持,而且能工作得很好。

第二,如果 swap 的缺省实现效率不足(这几乎总是意味着你的类或模板使用了某种 pimpl idiom 的变种),就按照以下步骤来做:

提供一个能高效地交换你的类型的两个对象的值的 public 的 swap 成员函数。出于我过一会儿就要解释的动机,这个函数应该永远不会抛出异常。在你的类或模板所在的同一个 namespace 中提供一个非成员的 swap。用它调用你的 swap 成员函数。如果你写了一个类(不是类模板),就为你的类特化 std::swap。用它也调用你的 swap 成员函数。最后,如果你调用 swap,请确保在你的函数中包含一个 using 声明使 std::swap 可见,然后在调用 swap 时不使用任何 namespace 限定条件。

唯一没有解决的问题就是我的警告——绝不要让 swap 的成员版本抛出异常。这是因为 swap 的非常重要的应用之一是为类(以及类模板)提供强大的异常安全(exception-safety)保证。Item 29 将提供所有的细节,但是这项技术基于 swap 的成员版本绝不会抛出异常的假设。这一强制约束仅仅应用在成员版本上!它不能够应用在非成员版本上,因为 swap 的缺省版本基于拷贝构造和拷贝赋值,而在通常情况下,这两个函数都允许抛出异常。如果你写了一个 swap 的自定义版本,那么,典型情况下你是为了提供一个更有效率的交换值的方法,你也要保证这个方法不会抛出异常。作为一个一般规则,这两种 swap 的特型将紧密地结合在一起,因为高效的交换几乎总是基于内建类型(诸如在 pimpl idiom 之下的指针)的操作,而对内建类型的操作绝不会抛出异常。

Things to Remember

如果 std::swap 对于你的类型来说是低效的,请提供一个 swap 成员函数。并确保你的 swap 不会抛出异常。如果你提供一个成员 swap,请同时提供一个调用成员 swap 的非成员 swap。对于类(非模板),还要特化 std::swap。调用 swap 时,请为 std::swap 使用一个 using 声明,然后在调用 swap 时不使用任何 namespace 限定条件。为用户定义类型完全地特化 std 模板没有什么问题,但是绝不要试图往 std 中加入任何全新的东西。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有