Search code examples
c++c++11language-lawyerdefault-constructorvalue-initialization

Why does defining an empty copy ctor beside a deleted default ctor make a value initialization with empty list fail?


In short, why the code below behaves like described in the comments?

struct A
{
    A() = delete;
    //A(const A&) {} // uncommenting this...
};

int main()
{
    A a{}; // ... breaks this
    //A(); // this fails in either case because of `A() = delete;` only
}

What part of the standard (or at least a page on cppreference) should I look at to understand this?

However, writing A(const A&) = default; instead of //A(const A&) {} doesn't break A a{};. What about this? I think the underlying cause is the same, but a word from who really knows C++ is better than what I think.


Solution

  • Without the user-provided copy constructor, A is an aggregate. Yes, even though we deleted the default constructor. It's something that was addressed in C++20.

    So, prior to C++20, A a{}; is aggregate initialization, and so doesn't use the deleted constructor. When you uncomment the copy constructor, A stops being an aggregate, thus turning the aggregate initialization into value initialization. So the initialization of a will attempt to call the deleted constructor.

    To divine the meaning of an initializer from the standard, one typically starts at [dcl.init]/16. Going through the bullets, one can find how the properties of the initializer (when matched with the properties of the types in question) will affect the way initialization occurs.