移动后右值是否有效?
Should the rvalue be valid after a move?
假设我们有以下类:
class Foo
{
public:
Foo() { _bar=new Bar };
Foo(const Foo &right) { _bar=new Bar(right.bar); };
Foo(Foo &&right) { _bar=right._bar; right.bar=new Bar(); };
~Foo() { delete _bar; }
Foo &operator=(const Foo &right) { _bar->opertor=(right.bar); return *this;}
Foo &operator=(Foo &&right) { std::swap(_bar, right._bar); return *this;}
void func() { _bar->test=1 };
private:
Bar *_bar;
};
将其更改为以下内容并期望最终用户知道在执行移动后右值不再有效(因为调用赋值运算符以外的任何内容都可能崩溃)是否合法?
class Foo
{
public:
Foo() { _bar=new Bar };
Foo(const Foo &right) { _bar=new Bar(right.bar); };
Foo(Foo &&right) { _bar=right._bar; right.bar=nullptr; };
~Foo() { if(_bar != nullptr) delete _bar; }
Foo &operator=(const Foo &right)
{
if(_bar == nullptr)
_bar=new Bar();
_bar->opertor=(right.bar);
return *this;
}
Foo &operator=(Foo &&right)
{
if(_bar != nullptr)
delete _bar;
_bar=right._bar;
right._bar=nullptr;
return *this;
}
void func() { _bar->test=1 };
private:
Bar *_bar;
};
我的担忧来自这样一个事实,即 func(以及类中的所有其他函数)假设_bar存在。
原则上,它可能会变得无效,尽管您可能需要考虑将其保留为可分配状态(您的原始实现(因此经过编辑)没有这样做)。这将遵循标准库的政策,该政策规定:
除非另有指定,否则所有已从中移动的标准库对象都将处于有效但未指定的状态。也就是说,只有没有前置条件的函数(如赋值运算符)才能在对象从中移动后安全地在对象上使用
我建议重新实现赋值运算符,以便它将"this"对象与新构造的对象交换。这通常是避免在实现分配时引入错误行为的好方法。
移自对象应处于有效但未指定的状态。 请注意,这是一个建议,但不是标准的绝对要求。
如果之后对第二个代码执行正常操作(特别是复制赋值运算符),则第二个代码将中断。
如果_bar == nullptr
是有效状态,则您的复制赋值运算符有错误; 如果它不是一个有效的状态,那么我会说你的移动构造函数被窃听了。
铌。在第二个代码中,析构函数中的if
检查是多余的,因为删除空指针是合法的。
更改为以下内容并期望最终用户知道在执行移动后右值不再有效是否合法?
你当然应该这样做。不应将右值引用保持在可用状态。移动构造函数和移动赋值运算符背后的想法是,您正在从对象中移动有用的数据。不过,我不会再用"不再有效"来描述它。从C++的角度来看,它仍然是一个有效的对象,就像nullptr
是一个有效的指针一样。
第二个版本更惯用。从对象移出后,假定将不再使用该对象。
在指针上调用delete
之前检查nullptr
是不必要的,因为它在标准中定义为不执行任何操作。
如果用户希望使用移自对象,他的工作是确保它处于有效状态(即从有效对象进行赋值)。
您应该为Foo
记录每个成员函数具有的前提条件,例如:
void Foo::func();
要求:
*this
未处于移出状态。
然后,您可以为需要使用Foo::func()
的任何客户提供服务,除非...
如果您将Foo
与需要func()
的其他人的库一起使用,并且不记录类似"除非处于移出状态"之类的内容,那么您就不走运了。
例如:假设您的Foo
具有如下operator<
:
bool operator<(const Foo& x, const Foo& y);
要求:
x
和y
都不能处于移出状态。
现在,如果您执行以下操作:
std::vector<Foo> v;
// ... fill v
std::sort(v.begin(), v.end()); // oops!
上面的最后一行要求Foo
LessThanComparable
Foo
是否处于移自状态。 std::lib 中的所有通用代码都是如此。 std 代码的 require 子句适用于用户提供的代码,并且不会对移自对象进行例外。
但是,如果operator<
在任一参数处于移自状态时正常工作,则调用std::sort
就可以了。 即使func()
仍然要求Foo
不处于移出状态,也是如此。 这是因为std::sort
根本不需要func()
工作。
因此,总而言之,这完全取决于您Foo
与哪些其他代码交互。 也许没关系,也许不是。 记录Foo
的作用,并了解Foo
使用的代码的要求。
- 如果变量名称不跟在 char* 后面,const char* 是否有效?
- 钳制迭代器是否有效
- 检查由括号、方括号和大括号组成的一组方括号是否有效?
- 在函数内创建的对象的范围 - 如果在函数外部存储和访问引用,它们是否有效?
- 模板签名解析为 void(void) 被 GCC 拒绝;这是否有效C++?
- 我如何知道作为参数的size_t在函数中是否有效?
- 是否可以在C++中获取 CHAR 的有效十六进制地址?
- 我的运算符重载是否有效<<(流插入)左操作数不是 ostream
- C++ 返回指向函数内定义的静态数组的指针是否有效?
- 模板变量是否允许在多个翻译单元中并有效合并?
- 错误:在尝试检测 std::cout 是否<< t 时,功能强制转换为数组类型;有效
- 声明后,gcc 的动态大小数组是否与标准数组有效相同?
- 此递归模板类型定义是否有效C++?
- 将 C 函数转换为 C++ 以检查数字是否有效
- 函数参数的名称与调用函数时使用的变量相同是否有效?
- 如何检查输入是否有效?
- 如何检查用户的输入是否有效以及我正在寻找的数字?
- 堆分配对于大型块中的分页是否更有效?
- 在函数中按值传递 unordered_map/unordered_set 是否有效? C++
- 如何检查isupper(cstr)是否有效?