Aggregate initialization does not uphold constructor access [duplicate]
Asked Answered
L

1

7

Given the example here below, I was surprised to find that despite the default constructor explicitly being deleted (or made default for that matter), aggregate initialization remained possible.

#include <iostream>

struct DefaultPrivate
{
      const int n_;
      static const DefaultPrivate& create();

    private:
      DefaultPrivate() = delete;
};

const DefaultPrivate& DefaultPrivate::create()
{
    static DefaultPrivate result{10};
    return result;
}

int main() {
    DefaultPrivate x; //Fails
    DefaultPrivate y{10};//Works
    return 0;
}

Is the relation between private default (or deleted) construction and aggregate initialization unspecified in the standard?

This was the case on both GCC 6.3 and VCC 2017

The reason I'm asking the question, was that I hoped that changing access to the default constructor would prevent public aggregate initialization

Lilian answered 27/10, 2017 at 9:6 Comment(0)
J
6

From C++11, for list initialization,

If T is an aggregate type, aggregate initialization is performed.

And with C++11 an aggregate is one of the following types:

...

class type (typically, struct or union), that has

  • ...

  • no user-provided, inherited, or explicit (since C++17) constructors (explicitly defaulted or deleted constructors are allowed) (since C++11)

  • ...

That means from C++11, the class with explicitly deleted constructors is still considered as an aggregate type, then aggregate initialization is allowed.

And the effect is:

Each direct public base, (since C++17) array element, or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.

Note that for DefaultPrivate y{10};, in the above process the default constructor won't be considered at all, then the fact that it's declared as delete and private won't matter.

BTW: For DefaultPrivate x; default initialization is performed,

if T is a non-POD (until C++11) class type, the constructors are considered and subjected to overload resolution against the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial value for the new object;

so the default constructor is tried to be used but it's deleteed then compiling fails.

If you use aggregate initialization, like DefaultPrivate x{};, the code would work fine too; and n_ will be value initialized (and then zero initialized) as 0.

LIVE

Jankey answered 27/10, 2017 at 9:18 Comment(2)
Yes, the type is aggregate, but it's aggregate in the private sense, or I was hoping it would be, otherwise I wouldn't have used aggregate in titleLilian
@WernerErasmus Answer revised. The point is that the constructors won't be considered for this case, so it doesn't matter.Jankey

© 2022 - 2024 — McMap. All rights reserved.