重用已移动的容器?


84

重用已移动容器的正确方法是什么?

std::vector<int> container;
container.push_back(1);
auto container2 = std::move(container);

// ver1: Do nothing
//container2.clear(); // ver2: "Reset"
container = std::vector<int>() // ver3: Reinitialize

container.push_back(2);
assert(container.size() == 1 && container.front() == 2);

根据我在C ++ 0x标准草案中所读的内容;ver3似乎是正确的方法,因为移动后的对象位于

“除非另有规定,否则将这些移出的对象置于有效但未指定的状态。”

我从未发现过任何“以其他方式指定”的实例。

虽然我发现ver3有点round回,并且会更喜欢ver1,但是vec3可以允许进行其他一些优化,但另一方面很容易导致错误。

我的假设正确吗?


4
您可以调用clear,因为它没有先决条件(因此也不依赖于对象的状态)。
Nicol Bolas'2

@Nicol:假设有一个std::vector实现,它存储了一个指向其大小的指针(看起来很傻,但是合法)。从该向量移动可能会使指针为NULL,此后clear将失败。 operator=也可能失败。
Ben Voigt 2012年

9
@Ben:我认为这将违反“有效但未指定”的“有效”部分。
ildjarn

1
@ildjarn:我认为这只是意味着运行析构函数是安全的。
Ben Voigt 2012年

我想问题是什么是“有效”?
ronag 2012年

Answers:


97

从规范“有效但未指定状态”的第17.3.26节开始:

除了满足对象的不变量且对象上的操作按其类型指定的操作外,未指定的对象状态[示例:如果x类型的对象std::vector<int>处于有效但未指定的状态,x.empty()则可以无条件调用,并且x.front()可以调用仅当x.empty()返回false时。—末例]

因此,对象是活动的。您可以执行不需要先决条件的任何操作(除非您先验证先决条件)。

clear,例如,没有任何先决条件。并且它将使对象返回到已知状态。因此,只需清除并正常使用即可。


在标准的哪里可以找到std :: vector方法的“前提条件”?
ronag'2

1
@ronag:第23.2节包含列出这些表的表。
灰熊” 2012年

2
我发现以下有趣的内容:open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3241.html,他们写道:“容器可以比空更空”。
ronag'2

4
@ronag:1)如果容器处于有效状态,则调用clear有效。2)当容器在未指定的状态,主叫clear放容器到指定的状态,因为它已经在标准(§23.2.3表100)授权的后置条件。std::vector<T>具有push_back()始终有效的类不变式(只要TCopyInsertable)。
ildjarn 2012年

3
@ronag:open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3241.html引用了国家机构对“空无一空”的言论之一。国家机构的评论是不正确的。N3241没有提出这种状态。如果std :: container的实现确实由于移出而具有“空于空”的状态,则该状态必须是有效状态(即,您可以使用不需要任何先决条件的对象执行任何操作)。
Howard Hinnant 2012年

11

对象处于有效但未定义的状态基本上意味着虽然不能保证对象的确切状态,但它是有效的,因此只要不依赖这些成员函数(或非成员函数),就可以保证它们正常工作在具有一定状态的物体上。

所述clear()成员函数对对象,因此可以从移动对象上调用(比它是有效的,当然其他)的状态没有先决条件。另一方面,例如front()取决于容器不为空,因此不能被调用,因为不能保证容器为非空。

因此,ver2和ver3都应该很好。


载体将始终是空的,但是这不是一般的情况下也是如此,(IE阵列)
鸣叫鸭

“向量永远是空的”,您以此为基础吗?
ronag'2

1
@ronag:我的意思是当然是ver2和ver3(从文本中应该可以清楚地看到,修正了错字
Grizzly

有趣的是,front()仅针对声明了的前提条件std::array,甚至在表中也没有。
Ben Voigt 2012年

1
@Ben:§23.2.3表100表示,的操作语义front()*a.begin(),§23.2.1/ 6说“如果容器是空的,则begin() == end()”,和§24.2.1/ 5表示“图书馆从未假定过去-最终值是可取消引用的。因此,我认为front()可以推断出前提条件,尽管可以肯定地将其弄清楚。
ildjarn

-8

我认为您不能对移出的对象做任何事情(除了销毁它)。

您不能使用它swap来获得移动的所有优势,但将容器保持在已知状态吗?


+1。交换是一个好主意,尽管并非在所有情况下都有效,例如,使用自动将不起作用。也许在内部使用swap的safe_move是一个主意?
ronag'2

5
它是一个实时对象,您可以使用任何没有前提条件的函数(不变式除外)
Mooing Duck 2012年

的主要模板std::swap具有2个移动分配,这些分配的目标从值中移出。对我而言,这算是“对移出的物体做些什么”
Caleth
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.