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

1.1k views Asked by At

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

2

There are 2 answers

1
Dev Null On BEST ANSWER

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.

0
Fedor On

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