Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are C++ classes with virtual functions required to have a non-trivial copy constructor?

According to the C++ standard, a class that has virtual functions cannot have a trivial copy constructor:

A copy/move constructor for class X is trivial if it is not user-provided and if

— class X has no virtual functions (10.3) and no virtual base classes (10.1), and

— the constructor selected to copy/move each direct base class subobject is trivial, and

— for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial;

otherwise the copy/move constructor is non-trivial.

Now, imagine a class hierarchy that satisfies all the mentioned conditions except the 'no virtual functions' condition:

struct I
{
    virtual void f() = 0;
};

struct D final : I
{
   void f() override 
   {
   }
};

From an implementation's point of view those classes contain only a pointer to the virtual dispatch table. And the base class does not have any user-declared constructors, and the derived class is final, so the mentioned pointer can always have a constant value. Given all that, the copy constructor can be trivial, yet the standard explicitly forbids treating it as such.

On the other hand, the conditions of treating such classes as trivially destructible are met. The class does not declare a virtual destructor and uses implicitly-defined destructors.

Requiring triviality of destructors looks like mandating an optimization - implicitly defined destructors should not restore pointers to the virtual table in this case.

But such an optimization goes half-way in my opinion; classes with virtual functions still cannot be memcopied, even if they can be trivially destructed.

The questions:

  1. Are there any reasons I did not think of from an implementation perspective why such classes should have non-trivial copy-constructors?

  2. Are there any reasons the restrictions on triviality for copy-constructors cannot be relaxed in the standard?

like image 524
Григорий Шуренков Avatar asked Nov 18 '17 08:11

Григорий Шуренков


1 Answers

The copies and moves of polymorphic classes cannot be trivial because it would break slicing copies and moves which copy a base type of an object's dynamic type. For example:

struct Base { virtual int f() { return 0; } };
struct D1 : Base { int f() override { return 1; } };
struct D2 : Base { int f() override { return 2; } };

int main() {
  D1 d1;
  D2 d2;
  Base b = d1; // if this copy constructor were trivial, b would have D1's vtable
  b.f();       // ...and this call would return 1 instead of 0.
  b = d2;      // Ditto: b would have D2's vtable
  b.f();       // ...and this call would return 2 instead of 0.
}
like image 134
Casey Avatar answered Oct 31 '22 03:10

Casey