默认构造函数应该在具有移动语义的RAII类中执行什么操作?

klo*_*ffy 8 c++ raii pod move-semantics c++11

移动语义对RAII类很有用.它们允许一个人编程,好像一个人有价值语义而没有沉重的副本.一个很好的例子是从函数返回std :: vector.然而,使用值语义进行编程意味着,人们会期望类型的行为类似于原始数据类型.这两个方面有时似乎不一致.

一方面,在RAII中,人们会期望默认构造函数返回完全初始化的对象,或者如果资源获取失败则抛出异常.这可以保证任何构造的对象都处于有效且一致的状态(即安全使用).

另一方面,对于移动语义,当对象处于有效但未指定的状态时存在一个点.类似地,原始数据类型可以处于未初始化状态.因此,使用值语义,我希望默认构造函数在此有效但未指定的状态下创建对象,以便以下代码具有预期的行为:

// Primitive Data Type, Value Semantics
int i;
i = 5;

// RAII Class, Move Semantics
Resource r;
r = Resource{/*...*/}
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,我都希望"重"初始化只发生一次.我想知道,这方面的最佳做法是什么?显然,第二种方法存在一些实际问题:如果默认构造函数在未指定状态下创建对象,那么如何编写一个获取资源的构造函数,但不采用其他参数?(标记调度会浮现在脑海中......)

编辑:一些答案质疑试图让你的类像原始数据类型一样工作的理由.我的一些动机来自亚历山大·斯捷潘诺夫(Alexander Stepanov)的"高效编程与组件"(Efficient Programming with Components),他讲述了常规类型 特别是,让我引用一下:

无论c [内置类型]中的自然惯用表达是什么,都应该是常规类型的自然惯用表达式.

他继续提供与上面几乎相同的例子.他的观点在这方面是否有效?我理解错了吗?

编辑:由于没有太多的讨论,我即将接受最高投票的答案.在默认构造函数中以"从 - 移动状态"状态初始化对象可能不是一个好主意,因为每个同意现有答案的人都不会期望这种行为.

Ben*_*ley 6

然而,使用值语义进行编程意味着,人们会期望类型的行为类似于原始数据类型.

关键字"喜欢".不是"相同的".

因此,对于值语义,我希望默认构造函数在这个有效但未指定的状态下创建一个对象

我真的不明白你为什么要这么想.对我来说,它似乎不是一个非常理想的功能.

关于这个的最佳做法是什么?

忘记这个想法,非POD类应该与原始数据类型共享此功能.这是错误的.如果没有合理的方法来初始化没有参数的类,那么该类不应该有一个默认的构造函数.

如果你想声明一个对象,但是暂时不能初始化它(可能在更深的范围内),那么就使用std::unique_ptr.


Jon*_*rdy 5

如果您接受对象通常应该通过构造有效,并且对象上的所有可能操作都应该仅在有效状态之间移动它,那么在我看来,通过使用默认构造函数,您只是说两件事之一:

  • 这个值是一个容器,或另一个具有合理"空"状态的对象,我打算改变它,例如,std::vector.

  • 该值没有任何成员变量,主要用于其类型 - 例如,std::less.

并不意味着移动对象必须具有与默认构造状态相同的状态.例如,std::string包含空字符串的""状态可能与移动的string实例不同.当您默认构造一个对象时,您希望使用它; 当你从一个物体移动时,绝大多数时候你只是摧毁它.

如何编写一个获取资源但不带其他参数的构造函数?

如果您的默认构造函数很昂贵并且没有参数,我会质疑为什么.它真的应该做这么贵的事吗?它的默认参数来自哪些全局配置?也许明确地传递它们会更容易维护.举个例子std::ifstream:用一个参数,它的构造函数打开一个文件; 没有,你使用open()成员函数.