Does it violate the standard for a non-default-constuctible struct to lack a user-defined constructor?
M

2

6

It is possible to define a struct (a) that has no user-defined constructors, and (b) for which a default constructor cannot be generated. For example, Foo in the below:

struct Baz
{
   Baz(int) {}
};

struct Foo
{
   int bar;
   Baz baz;
};

You can still create instances of Foo using aggregate initialization:

Foo foo = { 0, Baz(0) };

My normal compiler (VS2012) will grudgingly accept this, but it raises 2 warnings:

warning C4510: 'Foo': default constructor could not be generated.

warning C4610: struct 'Foo' can never be instantiated - user defined constructor required

Of course, I've just proved warning #2 wrong--you can still instantiate it using aggregate initialization. The online compilers I've tried are happy enough to accept the above, so I'm guessing VS2012 is just being overly-aggressive with this warning. But I'd like to be sure--is this code ok, or does it technically violate some obscure part of the standard?

Manysided answered 23/2, 2015 at 15:46 Comment(7)
That's certainly a perfectly cromulent aggregate, which can be instantiated as you describe, according to C++11. Either there was something obscure in older dialects (which I doubt, but can't confirm, being even less of a language-historian than a language-lawyer), or the compiler is too heavy-handed with its warnings.Obfuscate
Just to get rid of the warning, have you tried deleting the default constructor so the compiler doesn't attempt the impossible?Snigger
That's not a constructor-less struct. It has an implicitly declared copy constructor.Misreckon
@DaleWilson The deletion syntax isn't supported by VS2012, but if I'm convinced the code's ok, I can disable the warning with a pragma.Manysided
@Misreckon That's true (see my edit). Is there a succinct term for the type of constructor I have in mind?Manysided
@Manysided Perhaps "not default-constructible"?Kibitka
according to this answer C4610 is bogus , although the explanation seems to be behind a login-required thingyDg
T
2

The standard explicitly allows cases like Foo in [12.1p4]:

[...] If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted [...] A defaulted default constructor for class X is defined as deleted if:

[...]

  • any potentially constructed subobject, except for a non-static data member with a brace-or-equal-initializer, has class type M (or array thereof) and either M has no default constructor or overload resolution (13.3) as applied to M’s default constructor results in an ambiguity or in a function that is deleted or inaccessible from the defaulted default constructor

[...]

Baz has no default constructor, so the emphasised part above applies (emphasis mine).

There's nothing 'undefined' or 'ill-formed' about such cases. The implicitly declared default constructor is defined as deleted, that's all. You could do the same thing explicitly, and it would still be just as valid.

The definition for aggregates is in [8.5.1p1]. For C++14, it is:

An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).

The 'no user-provided' part allows you to use = delete on all constructors that could possibly be implicitly declared (making them user-declared, but not user-provided) and the class would still be an aggregate, allowing you to use aggregate initialization on it.

As for warning C4610, I've encountered it before myself and reported it. As you can see, it's been fixed in the upcoming version of VC++.

It may be worth mentioning that the example I used in the bug report is taken directly from the standard, where it's treated as well-formed ([12.2p5.4]:

struct S { int mi; const std::pair<int,int>& mp; };
S a { 1, {2,3} };

This is similar to your case, but here, the implicitly declared default constructor is defined as deleted because the class has a non-static member of reference type that has no initializer.

Granted, it's only an example, but I think it's an additional indication that there's nothing wrong with these cases.

Tomtoma answered 23/2, 2015 at 22:55 Comment(1)
I'd say that settles it (I'd also say it deserves more votes).Manysided
P
-1

That's not actually aggregate initialization, it's uniform, which is only recently supported by VS. This warning is simply them not correctly updating it to reflect that that type can now be uniform initialized.

Aggregates may not have user-defined non-defaulted non-deleted constructors, and the rules for aggregate UDTs are that each member must also be an aggregate. Therefore, Baz is not an aggregate and as a direct result, neither can Foo be.

Proptosis answered 23/2, 2015 at 22:25 Comment(1)
Eh? Foo is an aggregate (dcl.init.aggr/1) , and when an aggregate is initialized by an initializer list, it is called aggregate initialization (dcl.init.list/3). There's no requirement that an aggregate's members be other aggregates.Dg

© 2022 - 2024 — McMap. All rights reserved.