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
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.
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