copy constructor is implicitly deleted because the default definition would be ill-formed

15.6k Views Asked by At

I've got a class A (from a library over which I have no control) with a private copy constructor and a clone method, and a class B derived from A. I would like to implement clone for B as well.

The naive approach

#include <memory>

class A { // I have no control here
  public:
    A(int a) {};

    std::shared_ptr<A>
      clone() const
      {
        return std::shared_ptr<A>(new A(*this));
      }

  private:
    A(const A & a) {};
};

class B: public A {
  public:
    B(int data, int extraData):
      A(data),
      extraData_(extraData)
    {
    }

    std::shared_ptr<B>
    clone() const
    {
      return std::shared_ptr<B>(new B(*this));
    }

  private:
    int extraData_;
};

int main() {
  A a(1);
}

however, fails, since the copy constructor of A is private:

main.cpp: In member function ‘std::shared_ptr<B> B::clone() const’:
main.cpp:27:42: error: use of deleted function ‘B::B(const B&)’
     return std::shared_ptr<B>(new B(*this));
                                      ^
main.cpp:17:7: note: ‘B::B(const B&)’ is implicitly deleted because the default definition would be ill-formed:
 class B: public A {
       ^
main.cpp:14:5: error: ‘A::A(const A&)’ is private
     A(const A & a) {};
     ^
main.cpp:17:7: error: within this context
 class B: public A {

There might a way to make use of A::clone() for B::clone(), but I'm not sure how this would work exactly. Any hints?

3

There are 3 best solutions below

0
On

I presume it's a typo that your B has no public members at all, and that you're missing a public: before the definition of B::B(int,int).

The author of the class represented by your A apparently wants it to be cloneable but not copy constructible. That would suggest he or she wants all instances to live on the heap. But contrariwise, there's the public constructor A::A(int). Are you sure you are right about that?

It's plausible to suppose that the class can reveal enough information about a given instance to constitute another instance. E.g., putting a little more flesh on A:

class A {
public:
    A(int a) 
    : data_(a){};

    std::shared_ptr<A>
    clone() const
    {
        return std::shared_ptr<A>(new A(*this));
    }

    int data() const {
        return data_;
    }

private:
    A(const A & a) {};
    int data_;
};

And if that is true, then the public constructor would render it merely inconvenient to circumvent the private, undefined copy constructor:

A a0(1);
A a1{a0.data()};     // Inconvenient copy construction

So I'm less than confident that A faithfully represents the problem class. Taking it at face value, however, the question you need to answer is: Can you even inconveniently copy construct an A?

If not then you're stuck. If so, then you can use inconvenient copy construction of A to expressly define a conventional copy constructor for B, which is all you need. E.g.

class B: public A {
public:
    B(B const & other)
    : A(other.data()),extraData_(other.extraData_){}    

    B(int data, int extraData):
    A(data),
    extraData_(extraData)
    {
    }

    std::shared_ptr<B>
    clone() const
    {
        return std::shared_ptr<B>(new B(*this));
    }

    int extradata() const {
        return extraData_;
    }

private:
    int extraData_;
};

#include <iostream>

int main()
{
    B b(1,2);
    std::shared_ptr<B> pb = b.clone();
    std::cout << pb->data() << std::endl;
    std::cout << pb->extradata() << std::endl;
    return 0;
} 
6
On

You need to make the copy-constructor of A protected so that the derived class could use it:

protected:
    A(const A & a) { /*...*/ }

Hope that helps.

3
On

The reason the default definition of B's copy constructor is ill-formed is because - if it was permitted - it would invoke the private (therefore inaccessible to B) and not defined copy constructor of A.

Make A's copy constructor either protected or public, so it is accessible to B. Another (really bad) option is to declare class B as a friend of A. All possibilities would also require you to provide a definition for A's copy constructor.