Mar*_*wen 5 c++ swap unions c++11
我正在调整一些 C++03 代码以利用 C++11 的新可能性,特别是在 C++11 方式中引入移动语义。但是我遇到了一个struct让我头疼的地方,因为它包含一个匿名联合。它具有全局形式
struct tree
{ // data of |tree|
enum {tag0,tag1, tag2, ... } kind;
union
{ type1 field1;
type2 field2;
...
};
// constructors
tree () : kind(tag0) {} // default, empty state
tree (const& type1 x) : kind(tag1), field1(x) {} // variant 1
tree (const& type2 x) : kind(tag2), field2(x) {} // variant 2
...
~tree(); // recursively clean up any branches
// other methods of |tree|
...
}; // |struct tree|
Run Code Online (Sandbox Code Playgroud)
当然还有更有意义的名字,但在这里不是重点。struct 代表一种解析树,一种多重递归结构,可以在每个节点以不同的方式分支出来,因此使用union. 许多成员类型实际上包含指向另一个类型的指针tree;目前它们是由包含管理的原始指针(基本上是因为 C++03 不允许在联合中除了 POD 类型之外的任何东西)tree,但是现在 C++11 不再有这样的限制,我当然打算替换它们通过更结构化的值(使用智能指针)。
(请不要开始说我一开始就不应该使用联合;我认为这实际上是解决手头问题的比基于多态的解决方案更自然的解决方案,并且无论如何都有足够大的使用它的代码体,我不想从根本上改变方法。)
代码实际上已经定义了有限形式的移动语义(为了避免在自底向上构造树时必须一直进行深度复制),以set_from将复制到默认构造的方法的形式( so empty)tree结构,从另一个这样的结构通过引用传递的顶层(使用开关复制联合的正确活动)。在此之后,它设置kind=tag0在另一个中,以便防止与副本共享任何分支。把这个方法转换成C++11风格的移动构造函数并不难。
然而,也有一个移动赋值运算符可能会很好。我想使用需要我编写交换操作的复制和交换习语。与this question中建议的不同,我认为使用std::swap是不可能的,因为那将使用tree我试图定义的移动赋值运算符运算符;鸡和蛋的情况。所以我最好自己写点东西。
困难在于,由于两个节点可能具有不同的变体,因此不存在调用swap字段的问题。不能只复制或交换两个节点的联合组件作为一个整体,因为 (1) 联合组件本身不知道哪个变体是活动的,以及 (2) 联合是匿名的,所以我什至不能声明这样的操作。它必须在tree要定义交换的结构级别。一个memcpy基础的解决方案将目前可能虽然丑陋,但它甚至是不可能的,一旦工会的领域将不再是POD类型。
所以我有点想不通了。我可以想到一个解决方案,在两个节点的活动变体上进行两级切换,但我对前景并不感到兴奋。想到的另一件事是交换角色,并直接定义移动分配(这与移动构造没有太大区别,尽管它需要在销毁任何后代时将目的地设置为默认状态,就像在调用析构函数时一样,在移动源之前),然后使用临时的一次移动构造和两次移动分配以传统方式实现交换。具有讽刺意味的是,在这个解决方案中,移动分配是到保证处于空变体的节点中,但移动分配无论如何都必须处理更一般的情况。我很确定这个解决方案是否具有很强的异常安全性。
有没有我忽略的更简单的解决方案?
因此,不要使用复制和交换来进行移动分配。set_from按理说不应该扔,因为它的全部目的是避免分配资源。我假设它不是抛出的,或者可以做成的。
如果您还没有clear()函数,那么使用从析构函数(释放资源)或set_from(将源置于清晰或清晰状态)中获取的代码来实现一个函数,并提供强大的异常保证。那么这个移动分配提供了强有力的异常保证:
tree &operator=(tree &&other) {
clear();
set_from(other);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
您需要移动构造和分配都处于other可以调用它的状态set_from,因此要么确保这一点,要么在调用后做一些额外的工作set_from。other.clear()应该这样做,但可能有点矫枉过正。
其他一切都很顺利。std::swap一旦您进行了工作移动构建和分配,就会立即工作,但可能略低于最佳状态。您也许可以改进它,因为您知道(正如您已经确定的那样)所使用的移动目标std::swap不需要被清除,因为它们刚刚被移动,并且您的移动实现已经使它们变得清晰。
您可能还想优化 where 的情况kind == other.kind,因此所需要的只是swap相关字段的 a 。
| 归档时间: |
|
| 查看次数: |
805 次 |
| 最近记录: |