user1011792
user1011792

Reputation:

c++ Inheriting private copy constructor: how doesn't this yield a compile time error?

In C++, if we have this class

class Uncopyable{
public:
    Uncopyable(){}
    ~Uncopyable(){}
private:
    Uncopyable(const Uncopyable&);
    Uncopyable& operator=(const Uncopyable&);
};

and then we have a derived class

class Dervied: private Uncopyable{
};

My question is: why won't this generate a compile time error when the compiler generates the default copy constructor and assignment operators in the derived class ? Won't the generated code try to access base class private members ?

Upvotes: 4

Views: 1875

Answers (6)

Casey
Casey

Reputation: 42554

C++11 12.8/7 states "If the class definition does not explicitly declare a copy constructor, one is declared implicitly." so Dervied has an implicitly declared copy constructor. 12.8/11 says:

An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (8.4.3) if X has:

  • a variant member with a non-trivial corresponding constructor and X is a union-like class,

  • a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,

  • a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,

  • any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,

  • for the copy constructor, a non-static data member of rvalue reference type, or

  • for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.

Specifically, the third bullet applies: Dervied has a direct base class Uncopyable that cannot be copied because overload resolution results in a function that is inaccessible from Dervied::Dervied(const Dervied&). As a result Dervied's implicitly declared copy constructor is declared as deleted, resulting in a compile time error if and only if that copy constructor is called.

Upvotes: 3

Sqeaky
Sqeaky

Reputation: 1936

One class cannot call private methods on another class, but it can inherit as much as it is coded too. This code just includes the member functions from Uncopyable in Derived.

Imagine if you wrote a class inheriting from std::vector. You can still erase, insert, push_back and do all those sorts of things. Because these are all public or protected vector member functions, they in turn call implementation specific private functions that do the low level things like manage memory. Your code in this derived class couldn't call those memory management functions directly though. This is used to insure the creators of the vector can change the internal details freely without breaking your use of the class.

If your example is what the code actually looks like, then this it is a common pattern used to make things that cannot be copied. It would make code like the following produce a compiler error:

Derived Foo;
Derived Bar;
Foo = Bar

It would also make the code throw an error on the following:

int GetAnswer(Derived bar)
    { /* Do something with a Derived */ }
Derived Foo;
int answer = GetAnser(Foo);

This example fails because a copy of foo is made and passed as the parameter in the function GetAnswer.

There are many reasons why something might not be copyable. The most common I have experienced is that the object manages some low level resource a single file, an opengl context, an audio output, etc... Imagine if you had a class that managed a log file. If it closed the file in the deconstructor, what happens to the original when a copy is destroyed.

Edit: to pass an Uncopyable class to a function, pass it by reference. The Following function does not make a copy:

int GetAnswer(Derived& bar)
    { /* Do something with a Derived */ }
Derived Foo;
int answer = GetAnser(Foo);

It would also cause a compiler error if all the constructor were private and the class was instantiated. But even if all the member function even constructors were private and the class was never instantiated that would be valid compiling code.

Edit: The reason a class with constructor is that there maybe other way to construct it or it maybe have static member functions, or class functions.

Sometimes factories are used to build object which have no obvious constructor. These might have functions to whatever magic is required to make the umakeable class instance. The most common I have seen is just that there was another constructor that was public, but not in an obvious place. I have also seen factories as friend classes to the unconstructable class so they could call the constructors and I have seen code manually twiddle bits of memory and cast pointers to the memory it to an instance of a class. All of these patterns are used to insure that a class is correctly created beyond just the guarantees C++ supplies.

A more common pattern I have seen is static member functions in classes.

class uncreateable
{
    uncreateable() {}
  public:
    static int GetImportantAnswer();
};

Looking at this it can be seen that not only do I not need to create a instance of the class to call GetImportantAnswer() but I couldn't create an instance if I wanted. I could call this code using the following:

int answer;
answer = uncreateable::GetImportantAnswer();

Edit: Spelling and grammar

Upvotes: 1

Etherealone
Etherealone

Reputation: 3558

The derived class will inherit the private copy constructor but will not need to use it unless you copy an object of derived type, as in this example.

The compiler does not auto-generate constructors/operators unless they are used and no other constructor/operator can be used to do that operation (i.e. a copy operation can be used in some situations where a move operation would suffice). The latter statement results in the following set of rules.

Here are the rules to the auto-generation of certain member functions:

  • Default constructor (if no other constructor is explicitly declared)

  • Copy constructor if no move constructor or move assignment operator is explicitly declared. If a destructor is declared generation of a copy constructor is deprecated.

  • Move constructor if no copy constructor, move assignment operator or destructor is explicitly declared.

  • Copy assignment operator if no move constructor or move assignment operator is explicitly declared. If a destructor is declared generation of a copy assignment operator is deprecated.

  • Move assignment operator if no copy constructor, copy assignment operator or destructor is explicitly declared.

  • Destructor

The list is taken from this Wikipedia page.

Upvotes: 1

Ashalynd
Ashalynd

Reputation: 12563

Well, actually this program does not compile with g++:

#include <iostream>
using namespace std;

class Uncopyable{
  public:
    Uncopyable(){}
    ~Uncopyable(){}
  private:
    Uncopyable(const Uncopyable&) {cout<<"in parent copy constructor";}
    Uncopyable& operator=(const Uncopyable&) { cout << "in parent assignment operator";}
};

class Derived: private Uncopyable{

};

int main() {
  Derived a;
  Derived b = a;
}

compiler output:

$ g++ 23183322.cpp 
23183322.cpp:10:88: warning: control reaches end of non-void function [-Wreturn-type]
    Uncopyable& operator=(const Uncopyable&) { cout << "in parent assignment operator";}
                                                                                       ^
23183322.cpp:13:7: error: base class 'Uncopyable' has private copy constructor
class Derived: private Uncopyable{
      ^
23183322.cpp:9:5: note: declared private here
    Uncopyable(const Uncopyable&) {cout<<"in parent copy constructor";}
    ^
23183322.cpp:19:15: note: implicit copy constructor for 'Derived' first required here
  Derived b = a;
              ^
1 warning and 1 error generated.

Upvotes: 0

arayq2
arayq2

Reputation: 2554

why won't this generate a compile time error when the compiler generates the default copy constructor and assignment operators in the derived class ?

Because the compiler generates them only when they are needed by the code being compiled. Write some code using the derived class where the copy constructor and/or assignment operator are involved, and you will see the compile-time error you are looking for.

Upvotes: 2

Paul Evans
Paul Evans

Reputation: 27567

The private in the inheritance makes them private to Derived, it can still see them, classes that use Derived can't.

Upvotes: 1

Related Questions