Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't RVO applied to base class subobject initialization?

Why is the move constructor for Base mandatory in case of inheritance (class B) in the following code (both in gcc 7.2 and clang 4.0)? I would expect it not to be required with guaranteed copy elision in C++17, as in case of composition (class A).

struct Base {
    Base(Base&&) = delete;
    Base& operator=(Base&&) = delete;

    Base()
    {
    }
};

Base make_base()
{
    return Base{};
}

struct A {
    A() : b(make_base()) {} // <<<--- compiles fine

    Base b;
};

#ifdef FAIL
struct B : public Base {
    B() : Base(make_base()) {} // <<<--- "Base(Base&&) is deleted"
};
#endif

example

like image 794
Dev Null Avatar asked Sep 06 '17 02:09

Dev Null


2 Answers

According to Richard Smith:

This is a defect in the standard wording. Copy elision cannot be guaranteed when initializing a base class subobject, because base classes can have different layout than the corresponding complete object type.

like image 117
Dev Null Avatar answered Nov 10 '22 15:11

Dev Null


This is C++ Standard Core Language issue 2403, which is currently in open status.

The example given there is almost identical to yours:

  struct Noncopyable {
    Noncopyable();
    Noncopyable(const Noncopyable &) = delete;
  };

  Noncopyable make(int kind = 0);

  struct AsBase : Noncopyable {
    AsBase() : Noncopyable(make()) {} // #1
  };

  struct AsMember {
    Noncopyable nc;
    AsMember() : nc(make()) { }  // #2?
  };

And the comment says

All implementations treat #1 as an error, invoking the deleted copy constructor, while #2 is accepted. It's not clear from the current wording why they should be treated differently.

Actually all implementations treat #1 as an error is not quite true, since Visual Studio compiler performs copy elision here and accepts both this example and yours. Demo: https://gcc.godbolt.org/z/G61fKT55K

like image 1
Fedor Avatar answered Nov 10 '22 15:11

Fedor