删除copy-ctor和copy-assignment - public、private还是protected?
Posted
技术标签:
【中文标题】删除copy-ctor和copy-assignment - public、private还是protected?【英文标题】:Deletion of copy-ctor & copy-assignment - public, private or protected? 【发布时间】:2019-08-07 21:46:05 【问题描述】:为了使对象不可复制,我们可以显式删除其复制构造函数和复制赋值运算符。
我的问题是:在课堂的public
、private
或protected
部分,什么是正确的做法?而且 - 这个选择有什么不同吗?
【问题讨论】:
function_name() = delete;
是 C++11 的新手。如果你想支持 C++98/03 就不能用了。
如果你把旧鞋扔掉,你会考虑把它们放在哪里吗?
@Klaus:不,但你想想把它们扔到哪里......
【参考方案1】:
delete
d 函数的访问是无关紧要的。事实上,对于类成员来说,添加一个额外的访问说明符 (delete:
) 会更有意义。我怀疑他们不这样做的原因是它不适用于非成员函数。
对于像复制构造函数这样的东西,将它放在public
部分在风格上更有意义。一个类没有复制构造函数这一事实是了解该类接口的一个非常重要的事实。
对于您将特定重载声明为已删除以在编译器时检测错误的内部函数,在与所有其他重载相同的部分中声明该函数是有意义的。
【讨论】:
【参考方案2】:delete
与private
访问一样好。
delete
的作用是如果函数被重载决议选择,则会导致错误。
private
的作用是如果函数是通过类外部或其朋友的重载决议选择的,则会导致错误。
如果两个错误都适用,则最终结果是相同的,但public
可能有助于避免编译器关于访问权限的消息,这可能会导致混淆。
【讨论】:
建议将删除的功能公开。见***.com/a/18931192/108238。 Clang-Tidy 也建议这样做。【参考方案3】:我们把删除的定义放在哪里有什么不同吗?
从纯语言的角度来看,它绝对是零差异。名称查找和重载解析发生在访问检查之前。并且尝试在重载决议结束时引用已删除的函数会使您的程序格式错误。编译器可能会也可能不会发布关于可访问性的另一个诊断,但程序已经有一个必须报告的错误。
因此,您可以将已删除的定义与您想要的任何可访问性一起放置。我认为大多数人会将其保持私有,以符合使类不可复制的“旧”做法(将这些成员的声明放在类的私有部分,而不是定义它们),如果只是为了帮助那些谁知道旧方法会更快“得到它”。混合成语,如果你愿意的话。
如果您需要同时支持 C++03 和 C++11 模式,标记为私有也是您无法避免的。在宏的帮助下,可以轻松地使标题符合这两个标准:
#if __cplusplus >= 201103L
#define DELETED_DEFINITION = delete
#else
#define DELETED_DEFINITION
#endif
class noncopyable
private:
// This header can be compiled as both C++11 and C++03
noncopyable(noncopyable const&) DELETED_DEFINITION;
void operator=(noncopyable const&) DELETED_DEFINITION;
;
【讨论】:
如果你想要向后兼容,那么这是必须的。 @LightnessRacesinOrbit 如果你想要向后兼容,你不应该使用 C++11 特性。在删除函数的情况下,解决方法由 StoryTeller 提出。但是在使用 lambdas、stl、并发等时,向后兼容的解决方案是什么? @hsalimi 如果您想要一个有用的兼容层,那么到目前为止,您只能使用 C++11 功能,没错,但我在之前的项目中确实有一个事件框架库,可以在其中编译无论是 C++03 还是 C++11 模式(它仍然被遗留嵌入式项目使用),在后一种情况下,它有一堆优化(主要与右值引用有关),在不破坏接口的情况下极大地改进了事情很糟糕。我确实需要它在 Boost.Thread 和std::thread
之间切换,但在后一种情况下,它减轻了库(和链接!)依赖,所以它不是徒劳的。
@hsalimi 但是是的,这意味着我不能在库中使用 lambdas ......或者,至少这样做会带来更多的麻烦。【参考方案4】:
什么是合适的地方——在班级的公共、私人或受保护的部分?
我会将它们放在public
部分。
这是因为删除构造函数或赋值运算符与使它们成为private
/ protected
是正交的;当这些未被删除时,它们默认为public
。在我看来,将删除内容放在这两个部分之一似乎是在暗示“如果我没有删除它们,我会将它们设为私有/受保护”——这不是您想要传达的信息。
但请注意,编译器并不关心您将删除内容放在哪个部分。
【讨论】:
正是这个。过去我们将这些东西设为私有是为了拒绝人们访问它们,但是这总是一个黑客行为并且只是因为我们不能delete
他们。这种考虑不再起作用。我不记得“复制构造函数是私有的”诊断是否倾向于优先于“复制构造函数被删除”诊断(我对此表示怀疑),但即使它不改变访问级别也不是正确的做法因为你给出的原因。
@LightnessRacesinOrbit 我知道我已经看到一些编译器在函数是私有的并被删除时给出了这两个错误。关于私人访问的问题只是额外的噪音。
@aschepler Fair 确实
gcc 7.4,但不是 gcc 8.1:godbolt.org/z/udzwB2(所以我猜他们改进了)。
@aschepler: 嗯...你在那里切换了输出选项卡,所以看起来它几乎是 7.4 做正确的事情。无论如何,谢谢。【参考方案5】:
从 Scott Meyers 的书,Effective Modern C++ (Item 10),似乎最好将它们定义为 public:
按照惯例,被删除的函数被声明为公共的,而不是私有的。 这是有原因的。当客户端代码尝试使用成员时 函数,C++ 在删除状态之前检查可访问性。当客户 代码尝试使用已删除的私有函数,一些编译器抱怨 只是关于函数是私有的,即使函数的 可访问性并不真正影响它是否可以使用。这是值得的 在修改遗留代码以替换时牢记这一点 带有已删除成员的私有且未定义的成员函数,因为 将新功能公开通常会导致更好的错误 消息。
此外,我认为删除的复制构造函数/赋值应该是类接口的一部分,与所有类用户共享。此类信息不应通过保密来保密。
【讨论】:
Meyers 与 StoryTeller 的回答和 my tests 相矛盾。这仍然是个好建议,但我发现 einpoklum 的推理更胜一筹。 @LightnessRacesinOrbit 我在 VS2013 上进行了检查。错误消息与 g++ 不同,它正确地显示了错误。此外,从概念的角度来看,当一个类删除一个ctor/cctor等时,该类想对它的所有客户说:“嘿,这个成员被删除了,你不能使用它”。保密这一事实已不是什么秘密。 同意;这就是 einpoklum 的推理,我觉得这很好 ;)以上是关于删除copy-ctor和copy-assignment - public、private还是protected?的主要内容,如果未能解决你的问题,请参考以下文章
用户定义的 Copy ctor 和 copy-ctors 进一步向下链编译器错误?程序员脑筋急转弯?
std::any 用于仅移动模板,其中 copy-ctor 内的 static_assert 等于编译错误,但为啥呢?