6

P1008 ("Prohibit aggregates with user-declared constructors") has become part of the C++20 standard, in order to prevent surprising behavior when using aggregate initialization:

struct X {
  int i{42};
  X() = delete;
};

int main() {
  X x2{3}; // Compiles in C++17, error in C++20
}

I agree that the above X x2{3}; statement should not compile. However, all the examples justifying P1008 that I've encountered are not realistic at all - they are purely syntactical and basically meaningless foo/bar/baz code snippets.

What problem does P1008 solve in practice? I find it hard to imagine how I would end up writing something like the above X in a real program.

Deleting the default constructor in a C++17 aggregate without providing other constructors to initialize it seems unrealistic to me.

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • I feel like the ship has sailed for this particular question. For what it's worth, I was mildly surprised when I ran into this case the first time, leading to [this question](https://stackoverflow.com/q/37618213/2069064) (although now I don't remember the original example), but like... yeah. – Barry Nov 13 '19 at 15:54
  • @Barry: I don't want to write a counter-proposal. I'm just curious about the real-world cases affected by P1008... if any – Vittorio Romeo Nov 13 '19 at 15:56
  • @Barry: even your example is not convincing. Why would you have a class with a `=default` private constructor and all public data members? – Vittorio Romeo Nov 13 '19 at 15:59
  • @VittorioRomeo: Because anyone who is able to get the type should be able to access the variables. It's creating new values for the type that is prohibited outside of `private` access classes. If you're given access to an existing value, you can still work on it as normal. – Nicol Bolas Nov 13 '19 at 16:02

1 Answers1

5

The most obvious case is this:

struct X
{
private:
    X() = default;
};

X x{};

This is not a type which should be able to be initialized outside of a privately accessible context. But it can be.

Now, such types might seem silly, but they're actually useful for implementing private functions that work through forwarding functions. make_shared for example cannot call constructors declared private, even if you make the make_shared template a friend. So instead, you make the constructors public, but require that the user pass an instance of a type that can only be constructed by someone with private access. So X would either be a member type of the target class or X would make the target class a friend.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Assuming I understand correctly, you are referring to the "passkey idiom". Honestly, it doesn't seem very motivating for the impact P1008 had, but at least it is somewhat realistic. – Vittorio Romeo Nov 13 '19 at 16:04
  • @VittorioRomeo: What impact did it have, exactly? It seems to me that any code it breaks is code that isn't following the Rule of 0 and thus probably deserves to be broken. – Nicol Bolas Nov 13 '19 at 16:06
  • It is arguable whether "the rule of zero" is exactly "not specifying any special member function" or also includes "specifying all special member functions as `=default`". In my mental model these were equivalent. This is an example of impact: https://twitter.com/tcanens/status/1185379657350352897 – Vittorio Romeo Nov 13 '19 at 16:12
  • 1
    @VittorioRomeo: `array`'s destructor *should not be* unconditionally `noexcept`. To me, this seems to be an example of why this is a good thing. If a type cares enough about what the subobjects are doing that it wants to enforce `noexcept` destruction, then I would say that it's not a logical aggregation. An aggregate object ought to be functionally identical to declaring the contents of the aggregate as variables in the same scope. That *includes* `noexcept` destructors of those types. This is the feature working as intended to me. – Nicol Bolas Nov 13 '19 at 16:33
  • 1
    This type is trivial, so if you can name it you can bless an object into existence, private constructor or not. If you make the name private, then the ctor's privateness isn't very relevant. – T.C. Nov 14 '19 at 15:29