我对这个指南有很大的保留。即使知道这是指导方针,而不是规则,我仍然持保留意见。
假设您有一个类似于std::complex<double>
, 或的用户编写的类std::chrono::seconds
。它只是一个值类型。它不拥有任何资源,它很简单。假设它有一个非特殊成员的构造函数。
class SimpleValue
{
int value_;
public:
explicit SimpleValue(int value);
};
好吧,我也想SimpleValue
成为默认构造函数,并且我通过提供另一个构造函数来禁止默认构造函数,所以我需要添加那个特殊成员:
class SimpleValue
{
int value_;
public:
SimpleValue();
explicit SimpleValue(int value);
};
我担心人们会记住这个准则和理由:好吧,既然我提供了一个特殊的成员,我应该定义或删除其余的,所以这里......
class SimpleValue
{
int value_;
public:
~SimpleValue() = default;
SimpleValue();
SimpleValue(const SimpleValue&) = default;
SimpleValue& operator=(const SimpleValue&) = default;
explicit SimpleValue(int value);
};
嗯......我不需要移动成员,但我需要盲目地遵循聪明人告诉我的内容,所以我将删除那些:
class SimpleValue
{
int value_;
public:
~SimpleValue() = default;
SimpleValue();
SimpleValue(const SimpleValue&) = default;
SimpleValue& operator=(const SimpleValue&) = default;
SimpleValue(SimpleValue&&) = delete;
SimpleValue& operator=(SimpleValue&&) = delete;
explicit SimpleValue(int value);
};
我担心 CoreCppGuidelines C.21 会导致大量看起来像这样的代码。为什么这么糟糕?有几个原因:
1.
这比这个正确的版本更难阅读:
class SimpleValue
{
int value_;
public:
SimpleValue();
explicit SimpleValue(int value);
};
2.
它坏了。当您第一次尝试SimpleValue
按值从函数返回 a 时,您会发现:
SimpleValue
make_SimpleValue(int i)
{
// do some computations with i
SimpleValue x{i};
// do some more computations
return x;
}
这不会编译。错误消息将说明有关访问已删除成员的内容SimpleValue
。
我有一些更好的指导方针:
1.
了解编译器何时为您默认或删除特殊成员,以及默认成员会做什么。
这张图表可以帮助解决这个问题:

如果这张图表太复杂,我理解。它很复杂。但是,当它一次向您解释一点时,处理起来会容易得多。 希望我能在一周内更新这个答案,并附上我解释这张图表的视频的链接。 这是解释的链接,经过比我想要的更长的延迟(我的道歉):https ://www.youtube.com/watch?v=vLinb2fgkHk
2.
当编译器的隐式操作不正确时,始终定义或删除特殊成员。
3.
不要依赖已弃用的行为(上图中的红色框)。如果您声明了任何析构函数、复制构造函数或复制赋值运算符,则同时声明复制构造函数和复制赋值运算符。
4.
永远不要删除移动成员。如果这样做,充其量只是多余的。在最坏的情况下,它会破坏你的课程(如上SimpleValue
例所示)。如果您确实删除了 move 成员,并且这是多余的案例,那么您会迫使您的读者不断地查看您的课程,以确保它不是损坏的案例。
5.
对 6 个特殊成员中的每一个都给予温柔的关怀,即使结果是让编译器为您处理它(可能通过隐式禁止或删除它们)。
6.
将你的特殊成员以一致的顺序放在你的类的顶部(只有那些你想明确声明的),这样你的读者就不必去搜索它们了。我有我最喜欢的订单,如果您喜欢的订单不同,那很好。我的首选顺序是我在SimpleValue
示例中使用的顺序。
这是一篇简短的论文,其中包含这种类声明风格的更多理由。