Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 628
dlf Avatar asked Feb 23 '15 15:02

dlf


1 Answers

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.

like image 113
bogdan Avatar answered Oct 05 '22 01:10

bogdan