BitTickler
BitTickler

Reputation: 11885

Warning: Multiple copy constructors defined while deleting them

Environment: VS2015 Update 3, 64 bit (debug|release) compile

In the code below, if I uncomment the Maybe(X&) = delete line, I get the warning mentioned in the code below and in the title of the question.

Now, I am aware, that there are certain rules in C++(11?), which might render the explicit deletion of that constructor obsolete. Only, even after searching the web for a while, I could not find a definite rule, which would confirm, that if I only delete Maybe(const X&) = delete, the compiler will not auto generate the other copy constructor.

So my question is first and foremost: Can anyone point me to spot in the C++ specification, which clearly defines the rules for auto generation of copy constructors? Alternatively, some less official easy to remember rule of thumb on how to be certain of what will happen would also be welcome.

template <class X>
class Maybe
{
    X *m_just;
public:
    explicit Maybe(const X& x)
        : m_just(new X(x))
    {}
    Maybe()
        : m_just(nullptr)
    {}
    Maybe(const Maybe<X>&& other)
        : m_just(other.m_just)
    {
        other.m_just = nullptr;
    }
    Maybe(const Maybe<X>&) = delete;
    // If line below is uncommented, this produces the warning: 
    // warning C4521: 'Maybe<Int32>': multiple copy constructors specified
    // Maybe(Maybe<X>&) = delete; 

    ~Maybe()
    {
        delete m_just;
        m_just = nullptr;
    }
    // ... more members and code which are not related to question
    // ...
};

Please do not comment on the whole idea of that class. It is just private tinkering in my private lab... ;)

Upvotes: 3

Views: 475

Answers (3)

molbdnilo
molbdnilo

Reputation: 66371

The compiler will only generate one of Maybe(Maybe<X>&) and Maybe(const Maybe<X>&).
The conditions for the choice are listed in section 12.8, paragraph 8:

The implicitly-declared copy constructor for a class X will have the form X::X(const X&) if

  • each direct or virtual base class B of X has a copy constructor whose first parameter is of type const B& or const volatile B&, and
  • for all the non-static data members of X that are of a class type M (or array thereof), each such class type has a copy constructor whose first parameter is of type const M& or const volatile M&

Otherwise, the implicitly-declared copy constructor will have the form X::X(X&)

Or more informally, the parameter will be const if and only if everything that needs to be copied can be "const-copied".

Deleting one will not cause the other to be generated.

Since you have no base class and your only member is a pointer, the generated constructor will be of the const variety and you can leave out the line causing an error.

Upvotes: 3

Sebastian Redl
Sebastian Redl

Reputation: 71989

other.m_just = nullptr;

This line should not compile. Something must be wrong with your compiler, or your code example doesn't exactly reflect what you're compiling.

Anyway, the move constructor should take a non-const rvalue reference.

The commented line is simply not necessary. Just leave it out. If you define the copy constructor (even as deleted), no other form of the copy constructor is defined by the compiler, ever, so if you leave the line out, there simply is no constructor that takes a non-const reference to Self, meaning that overload resolution will just pick the const reference copy constructor, which is deleted anyway.

Upvotes: 0

Clinton
Clinton

Reputation: 23135

My C++ is a little bit rusty so this might be wrong, but I don't think you should even have a non-const copy constructor (except for the move constructor naturally).

If a = b changes b you're going to surprise some people. Perhaps it's legitimate for the copy constructor to change the source in a way that's not externally visible, but in that case I think you're better off using const_cast inside the const copy constructor.

The error I don't think is about delete, it's about defining multiple copy constructors, which isn't allowed. I suspect even allowing the non-const copy constructor is a bit of a wart in the language.

Upvotes: 0

Related Questions