c++direct-initialization

Why does direct initializing a base class member from a prvalue require a copy or move constructor?


It is well known that for a class with deleted copy and move constructors, it is still possible to direct initialize an object from a prvalue such as the return value of a function. For example, this compiles:

struct B {
  B(int i, std::string s) {}
  B(const B&) = delete;
};

B Factory() {
  return {1, "ss"};
}

int main() {
  B obj(Factory());
  return 0;
}

However, if I add:

struct D : public B {
  D() : B(Factory()) {}
};

I get an error: main.cpp:22:40: error: use of deleted function ‘B::B(const B&)’

Why is this? The standard says (11.9.4(7)):

The expression-list or braced-init-list in a mem-initializer is used to initialize the designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of 9.4 for direct-initialization.

And Section 9.4(17.6.1) covers the case of intializing by a prvalue of the same type, and it makes no exception for a base class member:

If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object.


Solution

  • You are correct that according to the standard there shouldn't be a copy/move constructor call here. The B subobject ought to be directly initialized by {1, "ss"} as in the other cases of mandatory copy elision you mentioned.

    However, that is not actually implementable because of the potential padding reuse of parts of the base class subobject that the function Factory doesn't know about and might therefore unintentionally overwrite.

    See CWG issue 2403.