移动构造函数和 `std::array`

25
根据N3485 §23.3.2.2的规定:
(...) array 的隐式移动构造函数和移动赋值运算符分别要求 T 必须是 MoveConstructible 或 MoveAssignable。
因此,只有当 std::array 的元素类型满足这些条件时,它才支持移动语义。很好!
然而,这到底意味着什么?我倾向于将其视为提供符合STL接口的更安全版本的数组,但如果这是真的,那么如何使用 std::array 移动构造它的元素?我能用普通的数组做同样的事情吗?
4个回答

28

可是,这真正意味着什么?

这意味着,如果元素类型是可移动的,那么数组类型也是可移动的。

std::array<movable, 42> move_from = {...};
std::array<movable, 42> move_to = std::move(move_from); // moves all the elements

我认为这种类型是一个更安全的数组版本,提供了符合STL标准的接口。

事实并非如此。它是一个数组的包装器,赋予它与聚合类相同的语义——包括复制和移动的能力。

std::array 如何移动构造其元素?

与任何其他聚合类一样。它的隐式移动构造函数将移动构造所有成员,包括任何成员数组的元素。

我可以用普通数组做同样的事情吗?

只有当你像 std::array 一样将其包装在类类型中时才可以。


1
只有将它包装在类类型中才行。所以说,正是因为它是用户定义的类型,才允许这种行为...很有趣。 - user2033018
9
确实 - 具体来说,用户定义的聚合类型具有隐式生成的移动构造函数。 - Mike Seymour

25
移动一个 std::array 与移动一个 std::vector 是不同的。当将一个 std::vector 移动到另一个时,有时可以仅重新定向内部指针而避免操作元素。

当然,对于 std::array,这是不可能的 - 它的元素具有自动存储期,它们实际上包含在对象内部。但是,每个单独的元素仍然可以被移动,这就是 std::array 上的移动操作所做的事情。

* 假设分配器是兼容的且不禁止此操作

** 当缓冲区不能仅由目标向量重新拥有时,std::vector 就会得到相同的结果。


7

对于非联合类而言,其默认的移动构造函数执行成员逐个移动。移动原始数组数据成员意味着移动该数组的每个元素,详见[class.copy]/15。

因此,您可以通过将原始数组放入一个类中来移动它:

struct wrap
{
    std::string arr[25];
};

auto w = wrap();
auto m = std::move(w); // moves the 25 `std::string`s

您还可以手动调用元素的移动构造函数,例如:
std::string a[3] = { /*...*/ };
std::string b[3] = {std::move(a[0]), std::move(a[1]), std::move(a[2])};

std::array是否包含原始数组并未指定。但是,它确实包含value_type的数据成员,因为它保证是一个聚合体。当调用移动构造函数时,这些数据成员将按照上述描述进行移动。

如果std::array的数据成员不可移动构造,则实例化其移动构造函数将失败。


1
因此,您可以通过将原始数组放入类中来移动它。正如Mike Seymour所指出的那样,我之前并不知道这一点 - 尽管现在想想,这很有道理。 - user2033018

0

我没想到! - user2033018

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接