使用c++ 11中显式删除的成员函数,仍然值得从不可复制的基类继承吗?

时间:2022-09-24 23:42:05

With explicitly deleted member functions in C++11, is it still worthwhile to inherit from a noncopyable base class?

使用c++ 11中显式删除的成员函数,仍然值得从不可复制的基类继承吗?

I'm talking about the trick where you privately inherit a base class which has private or deleted copy constructor and copy assignment (e.g. boost::noncopyable).

我正在讨论的技巧是,您可以私有地继承具有私有或已删除的复制构造函数和复制分配(例如boost::noncopyable)的基类。

Are the advantages put forward in this question still applicable to C++11?

在这个问题中提出的优点仍然适用于c++ 11吗?


I don't get why some people claim it's easier to make a class non-copyable in C++11.

我不明白为什么有些人声称在c++ 11中创建一个类是不受版权保护的。

In C++03:

在c++中03:

private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}

In C++11:

在c++中11:

MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;

EDIT:

编辑:

As many people have pointed out, it was a mistake to provide empty bodies (i.e. {}) for the private copy constructor and copy assignment operator, because that would allow the class itself invoke those operators without any errors. I first started out not adding the {}, but ran into some linker issues that made me add the {} for some silly reason (I don't remeber the circumstances). I know better know. :-)

正如许多人所指出的,为私有复制构造函数和复制赋值操作符提供空体(即{})是错误的,因为这会允许类本身调用这些操作符而不产生任何错误。我最初不添加{},但是遇到了一些链接器问题,使我出于一些愚蠢的原因添加了{}(我不记得当时的情况)。我知道更好的知道。:-)

5 个解决方案

#1


73  

Well, this:

嗯,这个:

private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}

Still technically allows MyClass to be copied by members and friends. Sure, those types and functions are theoretically under your control, but the class is still copyable. At least with boost::noncopyable and = delete, nobody can copy the class.

技术上仍然允许MyClass被成员和朋友复制。当然,这些类型和函数在您的控制下是理论上的,但是类仍然是可复制的。至少使用boost:::noncopyable和= delete,没有人可以复制类。


I don't get why some people claim it's easier to make a class non-copyable in C++11.

我不明白为什么有些人声称在c++ 11中创建一个类是不受版权保护的。

It's not so much "easier" as "more easily digestible".

与其说“更容易”,不如说“更容易消化”。

Consider this:

考虑一下:

class MyClass
{
private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}
};

If you are a C++ programmer who has read an introductory text on C++, but has little exposure to idiomatic C++ (ie: a lot of C++ programmers), this is... confusing. It declares copy constructors and copy assignment operators, but they're empty. So why declare them at all? Yes, they're private, but that only raises more questions: why make them private?

如果你是一个c++程序员,他读过c++的介绍性文本,但是很少接触到惯用c++ (ie:很多c++程序员),这是……让人困惑。它声明复制构造函数和复制分配操作符,但它们是空的。那么为什么要宣布它们呢?是的,它们是私密的,但这只会带来更多的问题:为什么要把它们保密?

To understand why this prevents copying, you have to realize that by declaring them private, you make it so that non-members/friends cannot copy it. This is not immediately obvious to the novice. Nor is the error message that they will get when they try to copy it.

要理解这为什么防止复制,您必须认识到,通过声明它们为private,您可以使非成员/朋友无法复制它们。对于新手来说,这不是显而易见的。当他们试图复制它时,也不会得到错误消息。

Now, compare it to the C++11 version:

现在,将它与c++ 11版本进行比较:

class MyClass
{
public:
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;
};

What does it take to understand that this class cannot be copied? Nothing more than understanding what the = delete syntax means. Any book explaining the syntax rules of C++11 will tell you exactly what that does. The effect of this code is obvious to the inexperienced C++ user.

如何理解这个类不能被复制?仅仅是理解= delete语法的含义。任何解释c++ 11语法规则的书都会告诉你它的作用。对于没有经验的c++用户来说,这段代码的效果是显而易见的。

What's great about this idiom is that it becomes an idiom because it is the clearest, most obvious way to say exactly what you mean.

这个习语的伟大之处在于它变成了一个习语,因为它是最清晰、最明显的表达你的意思的方式。

Even boost::noncopyable requires a bit more thought. Yes, it's called "noncopyable", so it is self-documenting. But if you've never seen it before, it raises questions. Why are you deriving from something that can't be copied? Why do my error messages talk about boost::noncopyable's copy constructor? Etc. Again, understanding the idiom requires more mental effort.

甚至boost::不可复制需要更多的思考。是的,它被称为“不可拷贝的”,所以它是自文档化的。但如果你以前从未见过它,它会引发问题。你为什么要从那些不能被复制的东西中获得?为什么我的错误消息谈到boost::noncopyable的复制构造函数?再一次,理解这个习语需要更多的脑力劳动。

#2


25  

The first thing is that as others before me point out, you got the idiom wrong, you declare as private and don't define:

首先,正如我之前的其他人指出的那样,你把这个习语搞错了,你把它声明为私有,没有定义:

class noncopyable {
   noncopyable( noncopyable const & );
   noncopyable& operator=( noncopyable const & );
};

Where the return type of the operator= can be basically anything. At this point, if you read that code in a header, what does it actually mean? It can only be copied by friends or it cannot be copied ever? Note that if you provide a definition as in your example, it is stating that I can be copied only inside the class and by friends, it is the lack of a definition what translates this into I cannot be copied. But the lack of a definition in the header is not a synonym for a lack of definition everywhere, as it could be defined in the cpp file.

操作符=的返回类型基本上可以是任何类型。此时,如果您在header中读取代码,它的真正含义是什么?它只能被朋友复制,还是不能复制?请注意,如果您提供了一个定义,如您的示例中所示,它表明我只能在类中被复制,而且只能被朋友复制,那么将它转换为I不能被复制的定义是缺乏的。但是header中缺少定义并不是缺少定义的同义词,因为它可以在cpp文件中定义。

This is where inheriting from a type called noncopyable makes it explicit that the intention is to avoid copies, in the same way that if you manually wrote the code above you should document with a comment in the line that the intention is disabling copies.

在这里,从一个名为noncopyable的类型继承可以明确地表明,这样做的目的是避免复制,就像如果您手工编写上面的代码,那么您应该在代码行中使用注释来记录,以便禁用副本。

C++11 does not change any of this, it just makes the documentation explicit in the code. In the same line that you declare the copy constructor as deleted you are documented that you want it fully disabled.

c++ 11没有改变任何这一点,它只是使文档在代码中变得显式。在您将复制构造函数声明为已删除的行中,您将文档化,以便完全禁用它。

As a last comment, the feature in C++11 is not just about being able to write noncopyable with less code or better, but rather about inhibiting the compiler from generating code that you don't want generated. This is just one use of that feature.

作为最后一个注释,c++ 11的特性不仅仅是能够用更少的代码或更好的方式编写不可复制的代码,而是要阻止编译器生成您不想生成的代码。这只是该特性的一个用途。

#3


10  

In addition to the points others have brought up...

除了别人提出的观点之外……

Having a private copy constructor and copy assignment operator that you do not define prevents anyone from making copies. However, if a member function or friend function attempts to make a copy, they will receive a link-time error. If they attempt to do so where you have explicitly deleted those functions, they will receive a compile-time error.

拥有一个没有定义的私有复制构造函数和复制分配操作符可以防止任何人复制。但是,如果一个成员函数或好友函数试图复制一个副本,则会收到一个链接时间错误。如果他们试图这样做,而您已经显式地删除了这些函数,他们将收到编译时错误。

I always make my errors occur as soon as possible. Make run-time errors happen at the point of error instead of later on (so make the error happen when you change the variable instead of when you read it). Make all run-time errors into link-time errors so the code never has a chance to be wrong. Make all link-time errors into compile-time errors to speed up development and have slightly more useful error messages.

我总是尽可能快地犯错误。使运行时错误发生在错误点而不是稍后发生(因此在更改变量时而不是读取变量时发生错误)。将所有运行时错误转换为链接时错误,这样代码就不会出错。将所有链接时间错误转换为编译时间错误,以加快开发速度,并提供更有用的错误消息。

#4


5  

It's more readable and allows the compiler to give better errors.

它可读性更强,允许编译器给出更好的错误。

The 'deleted' is more clear to a reader, especially if they're skimming over the class. Likewise, the compiler can tell you that you're trying to copy a non-copyable type, instead of giving you a generic 'trying to access private member' error.

“已删除”对读者来说更清晰,尤其是当他们浏览类时。同样,编译器可以告诉您您正在尝试复制一个不可复制的类型,而不是给您一个通用的“试图访问私有成员”错误。

But really, it's just a convenience feature.

但实际上,它只是一个方便的特性。

#5


2  

People here are recommending that you declare member functions without defining them. I would like to point out that such an approach isn't portable. Some compilers/linkers require that if you declare a member function then you must also define it, even if it isn't used. If you are using only VC++, GCC, clang then you can get away with this, but if you are trying to write truly portable code then some other compilers (e.g. Green Hills) will fail.

这里的人建议您声明成员函数而不定义它们。我想指出的是,这种方法是不可移植的。一些编译器/链接器要求,如果您声明一个成员函数,那么您也必须定义它,即使它没有被使用。如果您只使用vc++、GCC、clang,那么您就可以摆脱它,但是如果您正在尝试编写真正的可移植代码,那么其他一些编译器(例如,Green Hills)将会失败。

#1


73  

Well, this:

嗯,这个:

private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}

Still technically allows MyClass to be copied by members and friends. Sure, those types and functions are theoretically under your control, but the class is still copyable. At least with boost::noncopyable and = delete, nobody can copy the class.

技术上仍然允许MyClass被成员和朋友复制。当然,这些类型和函数在您的控制下是理论上的,但是类仍然是可复制的。至少使用boost:::noncopyable和= delete,没有人可以复制类。


I don't get why some people claim it's easier to make a class non-copyable in C++11.

我不明白为什么有些人声称在c++ 11中创建一个类是不受版权保护的。

It's not so much "easier" as "more easily digestible".

与其说“更容易”,不如说“更容易消化”。

Consider this:

考虑一下:

class MyClass
{
private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}
};

If you are a C++ programmer who has read an introductory text on C++, but has little exposure to idiomatic C++ (ie: a lot of C++ programmers), this is... confusing. It declares copy constructors and copy assignment operators, but they're empty. So why declare them at all? Yes, they're private, but that only raises more questions: why make them private?

如果你是一个c++程序员,他读过c++的介绍性文本,但是很少接触到惯用c++ (ie:很多c++程序员),这是……让人困惑。它声明复制构造函数和复制分配操作符,但它们是空的。那么为什么要宣布它们呢?是的,它们是私密的,但这只会带来更多的问题:为什么要把它们保密?

To understand why this prevents copying, you have to realize that by declaring them private, you make it so that non-members/friends cannot copy it. This is not immediately obvious to the novice. Nor is the error message that they will get when they try to copy it.

要理解这为什么防止复制,您必须认识到,通过声明它们为private,您可以使非成员/朋友无法复制它们。对于新手来说,这不是显而易见的。当他们试图复制它时,也不会得到错误消息。

Now, compare it to the C++11 version:

现在,将它与c++ 11版本进行比较:

class MyClass
{
public:
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;
};

What does it take to understand that this class cannot be copied? Nothing more than understanding what the = delete syntax means. Any book explaining the syntax rules of C++11 will tell you exactly what that does. The effect of this code is obvious to the inexperienced C++ user.

如何理解这个类不能被复制?仅仅是理解= delete语法的含义。任何解释c++ 11语法规则的书都会告诉你它的作用。对于没有经验的c++用户来说,这段代码的效果是显而易见的。

What's great about this idiom is that it becomes an idiom because it is the clearest, most obvious way to say exactly what you mean.

这个习语的伟大之处在于它变成了一个习语,因为它是最清晰、最明显的表达你的意思的方式。

Even boost::noncopyable requires a bit more thought. Yes, it's called "noncopyable", so it is self-documenting. But if you've never seen it before, it raises questions. Why are you deriving from something that can't be copied? Why do my error messages talk about boost::noncopyable's copy constructor? Etc. Again, understanding the idiom requires more mental effort.

甚至boost::不可复制需要更多的思考。是的,它被称为“不可拷贝的”,所以它是自文档化的。但如果你以前从未见过它,它会引发问题。你为什么要从那些不能被复制的东西中获得?为什么我的错误消息谈到boost::noncopyable的复制构造函数?再一次,理解这个习语需要更多的脑力劳动。

#2


25  

The first thing is that as others before me point out, you got the idiom wrong, you declare as private and don't define:

首先,正如我之前的其他人指出的那样,你把这个习语搞错了,你把它声明为私有,没有定义:

class noncopyable {
   noncopyable( noncopyable const & );
   noncopyable& operator=( noncopyable const & );
};

Where the return type of the operator= can be basically anything. At this point, if you read that code in a header, what does it actually mean? It can only be copied by friends or it cannot be copied ever? Note that if you provide a definition as in your example, it is stating that I can be copied only inside the class and by friends, it is the lack of a definition what translates this into I cannot be copied. But the lack of a definition in the header is not a synonym for a lack of definition everywhere, as it could be defined in the cpp file.

操作符=的返回类型基本上可以是任何类型。此时,如果您在header中读取代码,它的真正含义是什么?它只能被朋友复制,还是不能复制?请注意,如果您提供了一个定义,如您的示例中所示,它表明我只能在类中被复制,而且只能被朋友复制,那么将它转换为I不能被复制的定义是缺乏的。但是header中缺少定义并不是缺少定义的同义词,因为它可以在cpp文件中定义。

This is where inheriting from a type called noncopyable makes it explicit that the intention is to avoid copies, in the same way that if you manually wrote the code above you should document with a comment in the line that the intention is disabling copies.

在这里,从一个名为noncopyable的类型继承可以明确地表明,这样做的目的是避免复制,就像如果您手工编写上面的代码,那么您应该在代码行中使用注释来记录,以便禁用副本。

C++11 does not change any of this, it just makes the documentation explicit in the code. In the same line that you declare the copy constructor as deleted you are documented that you want it fully disabled.

c++ 11没有改变任何这一点,它只是使文档在代码中变得显式。在您将复制构造函数声明为已删除的行中,您将文档化,以便完全禁用它。

As a last comment, the feature in C++11 is not just about being able to write noncopyable with less code or better, but rather about inhibiting the compiler from generating code that you don't want generated. This is just one use of that feature.

作为最后一个注释,c++ 11的特性不仅仅是能够用更少的代码或更好的方式编写不可复制的代码,而是要阻止编译器生成您不想生成的代码。这只是该特性的一个用途。

#3


10  

In addition to the points others have brought up...

除了别人提出的观点之外……

Having a private copy constructor and copy assignment operator that you do not define prevents anyone from making copies. However, if a member function or friend function attempts to make a copy, they will receive a link-time error. If they attempt to do so where you have explicitly deleted those functions, they will receive a compile-time error.

拥有一个没有定义的私有复制构造函数和复制分配操作符可以防止任何人复制。但是,如果一个成员函数或好友函数试图复制一个副本,则会收到一个链接时间错误。如果他们试图这样做,而您已经显式地删除了这些函数,他们将收到编译时错误。

I always make my errors occur as soon as possible. Make run-time errors happen at the point of error instead of later on (so make the error happen when you change the variable instead of when you read it). Make all run-time errors into link-time errors so the code never has a chance to be wrong. Make all link-time errors into compile-time errors to speed up development and have slightly more useful error messages.

我总是尽可能快地犯错误。使运行时错误发生在错误点而不是稍后发生(因此在更改变量时而不是读取变量时发生错误)。将所有运行时错误转换为链接时错误,这样代码就不会出错。将所有链接时间错误转换为编译时间错误,以加快开发速度,并提供更有用的错误消息。

#4


5  

It's more readable and allows the compiler to give better errors.

它可读性更强,允许编译器给出更好的错误。

The 'deleted' is more clear to a reader, especially if they're skimming over the class. Likewise, the compiler can tell you that you're trying to copy a non-copyable type, instead of giving you a generic 'trying to access private member' error.

“已删除”对读者来说更清晰,尤其是当他们浏览类时。同样,编译器可以告诉您您正在尝试复制一个不可复制的类型,而不是给您一个通用的“试图访问私有成员”错误。

But really, it's just a convenience feature.

但实际上,它只是一个方便的特性。

#5


2  

People here are recommending that you declare member functions without defining them. I would like to point out that such an approach isn't portable. Some compilers/linkers require that if you declare a member function then you must also define it, even if it isn't used. If you are using only VC++, GCC, clang then you can get away with this, but if you are trying to write truly portable code then some other compilers (e.g. Green Hills) will fail.

这里的人建议您声明成员函数而不定义它们。我想指出的是,这种方法是不可移植的。一些编译器/链接器要求,如果您声明一个成员函数,那么您也必须定义它,即使它没有被使用。如果您只使用vc++、GCC、clang,那么您就可以摆脱它,但是如果您正在尝试编写真正的可移植代码,那么其他一些编译器(例如,Green Hills)将会失败。