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?

Nico Schlömer
  • 53,797
  • 27
  • 201
  • 249

3 Answers3

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;
} 
Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
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.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • I have no control over `A` since it comes from a library; I clarified this in the original post. – Nico Schlömer Jun 05 '15 at 11:27
  • 1
    Then you should not be deriving from `A`. – Peter Jun 05 '15 at 11:28
  • @NicoSchlömer: Ohh.. in that case, it is a bad idea to derive from `A`. You should use composition now, instead of inheritance. – Nawaz Jun 05 '15 at 11:28
  • 1
    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. – Peter Jun 05 '15 at 11:30
  • Is that a general rule: "Don't derive from classes with private copy constructors"? I don't quite understand why right now. – Nico Schlömer Jun 05 '15 at 11:31
  • @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*. – Nawaz Jun 05 '15 at 11:34
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.

Peter
  • 35,646
  • 4
  • 32
  • 74