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?

4

2 回答 2

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.

于 2015-02-23T22:55:45.317 回答
-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.

于 2015-02-23T22:25:00.263 回答