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

3

8

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?

Corking answered 5/6, 2015 at 11:19 Comment(0)
A
4

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;
} 
Astilbe answered 5/6, 2015 at 19:34 Comment(0)
S
2

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.

Sappy answered 5/6, 2015 at 11:25 Comment(6)
I have no control over A since it comes from a library; I clarified this in the original post.Dorsiventral
Then you should not be deriving from A.Splashboard
@NicoSchlömer: Ohh.. in that case, it is a bad idea to derive from A. You should use composition now, instead of inheritance.Sappy
It is a bad idea to derive from A at all. Having a private (and omitted) copy constructor is a pretty big hint from the developer that various things - including using A as a base class - are discouraged.Splashboard
Is that a general rule: "Don't derive from classes with private copy constructors"? I don't quite understand why right now.Dorsiventral
@NicoSchlömer: If the copy-ctor is private, then it means NOBODY, not even derived class, is allowed to make copy of the objects of such class. That in turn means, you cannot derive from it and at the same time, make it copable/clonable. If you want to derive from it, then you have to make the derived class non-copable/non-clonable as well.Sappy
S
1

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.

Splashboard answered 5/6, 2015 at 11:27 Comment(3)
I have no control over A since it comes from a library; I clarified this in the original post.Dorsiventral
As per comments I made in response to that clarification (below Nawaz's post) having a private constructor is a pretty significant hint that you should not be deriving from A.Splashboard
Is there no way to make use of A::clone() either?Dorsiventral

© 2022 - 2024 — McMap. All rights reserved.