安全到标准:移动会员?

Safe to std:move a member?

本文关键字:移动 标准 安全      更新时间:2023-10-16

已经找到了类似的问题,但不完全是这种情况。 以以下代码为例:

#include <iostream>
#include <string>
#include <vector>
struct Inner
{
int a, b;
};
struct Outer
{
Inner inner;    
};

std::vector<Inner> vec;
int main()
{
Outer * an_outer = new Outer;
vec.push_back(std::move(an_outer->inner));
delete an_outer;
}

这安全吗?即使这些是多态类或具有自定义析构函数的类?

我关注的是"Outer"的实例,它有一个成员变量"inner"移走了。据我所知,移动的东西不应该再被碰了。但是,这是否包括应用于outer的删除调用,并且在技术上也会调用inner上的删除(从而"触摸"它(?

std::move和更一般的移动语义都不会对对象模型产生任何影响。它们不会阻止对象存在,也不会阻止您将来使用这些对象。

他们所做的是要求从你"移动"的东西中借用封装的资源。例如,一个vector,它直接只存储指针一些动态分配的数据:该数据的所有权概念可以通过简单地复制该指针然后告诉vector将指针清空并且不再与该数据有任何关系来"窃取"。它屈服了。数据现在属于您。你有存在于宇宙中的最后一个指向它的指针。

所有这些都是通过一堆黑客实现的。第一个是std::move,它只是将您的vector表达式转换为vector&&,因此当您将其结果传递给构造或赋值操作时,将触发采用vector&&的版本(移动构造函数或移动赋值运算符(而不是采用const vector&的版本,并且该版本执行执行我在上一段中描述的操作所需的步骤。

(对于我们制作的其他类型的产品,我们传统上一直遵循这种模式,因为这样我们才能拥有好东西并说服人们使用我们的库。

但是你仍然可以使用矢量!你可以"触摸"它。你可以用它做什么可以从vector文档中找到,这延伸到任何其他可移动类型:对你使用移动对象的约束完全取决于它的类型,以及设计该类型的人所做的决定。

这些都对vector的生命周期没有任何影响。它仍然存在,它仍然需要记忆,到时候它仍然会被破坏。(在此特定示例中,您实际上可以.clear()它并重新开始将数据添加到新缓冲区。

因此,即使int对此有任何概念(他们没有;他们没有封装间接存储的数据,也没有资源;他们没有构造函数,所以他们也没有构造函数接受int&&(,"接触"它们delete将是完全安全的。而且,更一般地说,这些都不取决于您移动的东西是否是会员。

更一般地说,如果你有一个类型T,以及一个该类型的对象,并且你从它移动,并且T的约束之一是你在从它移动后无法delete它,那将是T中的一个错误。这将是T作者的一个严重错误。您的对象都需要是可破坏的。该错误可能表现为编译失败,或者更有可能表现为未定义的行为,具体取决于错误到底是什么。

tl;大卫:是的,这是安全的,有几个原因。

std::move是对右值引用的强制转换,它主要更改选择的构造函数/赋值运算符重载。在您的示例中,移动构造函数是默认生成的移动构造函数,它只是复制ints,因此不会发生任何事情。

这通常是否安全取决于您的类实现移动构造/分配的方式。例如,假设您的类持有一个指针。您必须将其设置为在移自类中nullptr,以避免在移自类被销毁时销毁指向的数据。

因为仅仅定义移动语义是一种自定义方式几乎总是会导致问题,所以五法则表示,如果您自定义以下任何一种:

  • 复制构造函数
  • 复制赋值运算符
  • 移动构造函数
  • 移动赋值运算符
  • 析构函数

您通常应该自定义所有内容,以确保它们的行为与呼叫者通常对您的班级的期望一致。