移动语义非常适合 RAII 类。它们允许人们像具有价值语义一样进行编程,而无需大量复制。一个很好的例子是从函数返回 std::vector。然而,使用值语义进行编程意味着,人们会期望类型表现得像原始数据类型。这两个方面有时似乎不一致。
一方面,在 RAII 中,人们期望默认构造函数返回一个完全初始化的对象,或者如果资源获取失败则抛出异常。这保证了任何构造的对象都将处于有效且一致的状态(即可以安全使用)。
另一方面,对于移动语义,存在一个点,即对象处于有效但未指定的状态。类似地,原始数据类型可以处于未初始化状态。因此,对于值语义,我希望默认构造函数在这个有效但未指定的状态下创建一个对象,以便以下代码具有预期的行为:
// Primitive Data Type, Value Semantics
int i;
i = 5;
// RAII Class, Move Semantics
Resource r;
r = Resource{/*...*/}
在这两种情况下,我都希望“重”初始化只发生一次。我想知道,这方面的最佳做法是什么?显然,第二种方法有一点实际问题:如果默认构造函数创建处于未指定状态的对象,那么如何编写一个获取资源但不带附加参数的构造函数?(想到标签调度......)
编辑:一些答案质疑试图让你的类像原始数据类型一样工作的基本原理。我的一些动机来自Alexander Stepanov 的 Efficient Programming with Components,其中他谈到了常规类型。特别是,让我引用:
无论是 c [用于内置类型] 中的自然惯用表达式,都应该是正则类型的自然惯用表达式。
他继续提供与上述几乎相同的示例。他的观点在这种情况下不成立吗?我理解错了吗?
编辑:由于没有太多讨论,我将接受最高投票的答案。在默认构造函数中将对象初始化为“从类似移动”状态可能不是一个好主意,因为同意现有答案的每个人都不会期望这种行为。