4

I am starting a new project in C++11, and just found out about the delete keyword that lets you prevent accidental calling of copy constructors and so on. Is there a "recommended" set of deletions I can do globally to increase type safety, such as preventing signed to unsigned cast within expressions? Should I default to deleteing all 5 operations that I can delete in all my classes?

FYI, this program requires high performance (thats why I'm using C++, for the first time in years) and there are very few times I want to copy anything, so a copy is usually a bug, although not 100% of the time, so I'm interested in this specific case, as well as the general case for other programs. I could potentially delete the copy constructor and add a separate method that copies the object for the rare time that I do need a copy. Would that be a good idea?

Drew
  • 12,578
  • 11
  • 58
  • 98
  • 2
    It really depends on your use-cases. Do you want to prohibit copying? Then mark the copy-constructor (and copy-assignment operator) as deleted. Don't do it on a regular generic basic, do it when needed only, that's my personal *opinion*. – Some programmer dude Mar 10 '15 at 07:07
  • 5
    Are you sure copying if a performance problem? That kind of thinking often leads to less performant code. – juanchopanza Mar 10 '15 at 07:10
  • What do you mean by deletions that you want to do 'globally'? – Paul Omta Mar 10 '15 at 07:23
  • Well, I have a lot of large objects (thousands of objects that are ~1MB each, so on my computer with 16GB of RAM it will be using a large fraction of the available RAM) – Drew Mar 10 '15 at 07:40
  • 1
    Also, you may take a look at [this very good cheat sheet](http://slideshare.net/ripplelabs/howard-hinnant-accu2014) – Rerito Mar 10 '15 at 08:06

3 Answers3

7

Is there a "recommended" set of deletions I can do globally to increase type safety, such as preventing signed to unsigned cast within expressions?

No. You certainly can't prevent integer conversions by deleting anything.

Should I default to deleteing all 5 operations that I can delete in all my classes?

No! A deleted destructor would make it impossible to destroy anything!

Also, deleting a move constructor rarely makes sense. If your type can be moved cheaply then allowing moves (e.g. when returning objects by value, or passing temporaries by value as function arguments) is usually a good thing, even if you don't want to allow copies. Artificially restricting users of the type from moving it when doing so is efficient is just annoying and doesn't provide any advantages.

You seem to be falling into the trap of seeing a feature is available and thinking you should use it. The following quote from The Old Man and the C seems relevant:

One final anecdote regarding the earlier story where someone used an int reference parameter. In discussing this paper, the programmer's comment was - "Well, the feature was in the language so I figured I should use it.". It is our belief that this is not a sufficient criteria for using a feature of C++. A feature should be used only when it can be demonstrated to be of benefit. A mountain is climbed "because it is there". The same should not hold true for C++ features. Their mere existence is not justification for use.

As for your final question:

I could potentially delete the copy constructor and add a separate method that copies the object for the rare time that I do need a copy. Would that be a good idea?

... maybe ... this might be a case where a deleted copy constructor can be demonstrated to be of benefit. But I would say generally no, don't do that, because it makes it difficult to store the object in containers, for example. It is better to just write your code carefully to avoid unwanted copies. If you're failing to do that and have demonstrated you really are making accidental copies, maybe reconsider deleting the copy constructor.

But you should not be deleting operations "by default" just because you've learnt about the feature. It should be much more common to use = default rather than = delete except for classes that model some atypical behaviour, such as unique-ownership of a resource.

Community
  • 1
  • 1
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • 1
    Well, `void f(int); template void f(T) = delete;`. No implicit conversion allowed because the template would be a better match. – T.C. Mar 10 '15 at 21:16
  • @T.C. true, but that won't help _within expressions_ as requested. You could write a whole suite of such functions and wrap every operation ... but at that point you might as well just use a different language without so many unsafe implicit conversions :) – Jonathan Wakely Mar 10 '15 at 22:39
5

In my experience, there are certain cases when deleting certain constructors or operators makes sense.

For instance, consider the unique_ptr class. unique_ptr's are used when one wants only one pointer to access some sort of data (perhaps in a multithreaded application), and thus it wouldn't make sense to have multiple unique_ptr's floating around that all point to the same object.

To prevent unwanted copy assignments, the following is included by default in the unique_ptr class:

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

That's one instance of when it would make sense to remove an operator from a class. I would say it's very much so a judgment call. My general rule is to ask myself, "is there absolutely no scenario in which the client of the class should be using x operator or y constructor?" If you then decide that x or y should in fact never (or perhaps only rarely) be used, then, in my opinion, it might make sense to go ahead and delete said operator/constructor.

CLL
  • 331
  • 1
  • 10
  • 1
    +1, and the corollary to your final paragraph is that you should **not** delete operations "by default", only when doing so is proven to be useful. – Jonathan Wakely Mar 10 '15 at 12:00
1

Yes, this idea seems to be good. Declaring all the unwanted special methods with delete will disable them, just like you want.

However, this was also possible before delete was invented - boost::noncopyable was used for that purpose before the release of c++11. Even with c++11, inheriting from boost::noncopyable seems to be easier (less typing) than marking all the unwanted special members with = delete.

In additon, since you mentioned "preventing signed to unsigned cast" - you should be aware that conversions between built-in types (like int to unsigned) are built-in and cannot be disabled. Rules for conversions that don't involve a user-defined type are the same in C and C++ (with a small number of exceptions), and cannot be changed. The best you can do is configure your compiler to "emit all warnings" and possibly "treat warnings as errors".

anatolyg
  • 26,506
  • 9
  • 60
  • 134
  • 1
    I would not recommend using boost::noncopyable over = delete. The latter clearly states the coder's intent, whereas introducing inheritance only obfuscates things. Besides, one should optimize for the reader, since code is read many more times that it is written. Less typing may therefore never be a reason to choose one construct over another. If your compiler only supports C++03, I would make the copy constructor and assignment operator private, to prevent copying. – Paul Omta Mar 10 '15 at 13:05
  • @PaulOmta The syntax for deleting the unwanted copying is `MyClass& operator=(MyClass) = delete; MyClass(MyClass&) = delete;`. The syntax for making a class not copyable is `: boost::noncopyable` (possibly introducing multiple inheritance of the benign nature). In my opinion, the *latter* clearly states the coder's intent and is more optimized for the reader, while the former is tricky to parse in one's head. It's in the eyes of the beholder though; maybe the additional complication of multiple inheritance can be confusing; I don't see other disadvantages. – anatolyg Mar 10 '15 at 13:18
  • Agreed, it's indeed personal preference (given that boost is available to you). I guess we both recommend differently. – Paul Omta Mar 10 '15 at 13:37
  • _"while the former is tricky to parse in one's head."_ It's still relatively new, you'll get used to it. – Jonathan Wakely Mar 10 '15 at 13:51