4

C++11 scratches an itch that's long bothered me by allowing you to mark implicitly compiler defined methods as verboten with the "= delete" syntax. Wikipedia for more info.

class Foo
{
public:
     Foo();
    ~Foo();

    // No copy
    Foo(Foo const &) = delete;
    Foo& operator=(Foo const &) = delete;
};

Copy and assignment operators for classes that I do not expect to have copied or assigned are always a pain to mess with. It's a lot of boiler-plate code to make them private and then often there's member data that don't have a default constructor that require some hand-waving to make the compiler happy on a function you just want no one to ever call.

class Bar
{
public:
   explicit Bar(UniqueResourceID id): m_data(id) { }
   ~Bar();

protected:
   SomeHandle  m_data; // no default constructor

// all this crap to keep from being able to copy.  Do not use any of these!!
private:
   Bar() { } // ERROR: m_data has no default constructor
   static UniqueResourceID s_invalidID; // now I'm making the problem worse,
                                        // because I don't actually need this
                                        // for anything real, except to shut
                                        // up some errors.
   Bar(Bar const &o): m_data(s_invalidID) { }
   Bar& operator =(Bar const &o): { return *this; }
};

Unfortunately, some of the compilers I have to use are not C++11 compilers and don't offer =delete. What's the best way of dealing with these? (Please tell me there's a better way than the second code snippet.)

  • 5
    Make them `private`. – πάντα ῥεῖ Oct 03 '14 at 18:58
  • 3
    Remove the `{}` from the `private` decl of `Bar() {}`. I.e. `Bar();` . You only need to declare it, not implement it. Goes for *all* of them. If you find after doing so your code doesn't *link* then something your friending or within `class Bar` is referring to the very things you're trying to shroud. In that case, *fix* the erroneous callers. – WhozCraig Oct 03 '14 at 18:58
  • Some consider deriving from `boost::noncopyable` or similar a superior method: It's concise and expresses the intent directly. See also: http://stackoverflow.com/q/7823990 – dyp Oct 03 '14 at 19:19
  • @dyp Boost isn't always available/desirable on all platforms, but it is a good point. Thanks for the suggestion. – Philippe Chaintreuil Oct 03 '14 at 19:30
  • 1
    @PhilippeChaintreuil Well it is quite simple to program your own: Essentially the same you're currently doing, but in a separate class. Something like `class noncopyable { noncopyable(noncopyable const&); noncopyable& operator=(noncopyable const&); public: noncopyable() {} };` – dyp Oct 03 '14 at 19:31
  • @dyp You should post that as an answer. – Philippe Chaintreuil Oct 03 '14 at 19:57

1 Answers1

7

You are writing all the extra crap because you are actually defining the body of the deleted operators -- I don't think you have to, and what I do is just making the declaration with no implementation anywhere, like this;

class Bar
{
public:
   explicit Bar(UniqueResourceID id): m_data(id) { }
   ~Bar();

protected:
   SomeHandle  m_data; // no default constructor

private:
   Bar(); 
   Bar(Bar const &o);
   Bar& operator =(Bar const &o);
};

This is no more verbose than writing the method with = delete appended to it.

Edit: Your definition of

....
private:
   Bar() {}

Is actually dangerous as it allows the operator to be called from other methods of within Bar without any errors ever getting generated (linker or compiler)

Soren
  • 14,402
  • 4
  • 41
  • 67
  • 2
    It should probably be mentioned that, while this is completely the correct answer, that the errors you get when you try to call them (especially within class scope) are different. You'll get access errors with attempted external/protected settings, and within class scope you'll get undefined symbol errors. – IdeaHat Oct 03 '14 at 19:07
  • That is correct, but I don't this there is any way around that in C++03. – Soren Oct 03 '14 at 19:11
  • @MadScienceDreams's statement is why I do that extra crap. I'd rather be told immediately where someone is using one of these methods when they shouldn't be instead of just getting a link error. A lot of linkers will just say you have an undefined reference, but not give you any indicator of where the heck that reference is. But: it is good to know if I'm in a bind, I can skip it if I'm feeling lazy. – Philippe Chaintreuil Oct 03 '14 at 19:27
  • 1
    @PhilippeChaintreuil -- but the linking errors will only come if you are doing the access of the operators from _within_ bar. Your _extra crap_ is actually dangerous as `private: Bar() { }` will allow the default constructor to be called from within `Bar` which is the opposite of what you want. – Soren Oct 03 '14 at 19:31
  • 1
    @PhilippeChaintreuil yeah, defining the function is precisely wrong because I can write static `void BARBAR(Bar& bar) {Bar b = bar;}`. The unhelpfulness of the errors is why they put in the deleted syntax, but Soren's answer is the old idiom. – IdeaHat Oct 03 '14 at 19:45