13

How to explain the difference, when I compile #if 0 and #if 1 versions of the following code:

#include <cstdlib>

struct A
{ 
    explicit A() = default; // explicitly defaulted or deleted constructors are allowed for aggregates (since C++11)
#if 1
private :
#endif
    int i;
};

int
main()
{
    A a = {};
    return EXIT_SUCCESS;
}
  • for #if 0 all is fine, compilation successful.
  • for #if 1 compilation failed with error message:

    error: chosen constructor is explicit in copy-initialization

What is the difference for expression A a = {}; depending on whether A is aggreagate or not?

Community
  • 1
  • 1
Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169
  • 2
    I think you answered it yourself. When you receive the error A is not an aggregate struct. So you can't initialize it using an initialization list. – Jonathan Nov 04 '15 at 13:25
  • @Jonathan My own understanding (dumb "it depends on aggregateness") is not quite clear. I want to hear exact explanation. – Tomilov Anatoliy Nov 04 '15 at 13:39

1 Answers1

9

TL;DR: Clang and GCC are wrong in rejecting your code. CWG 1630´s resolution made default-initialization well-formed regardless of the chosen default constructor being explicit or not.


In the variation of your code in which i is private, A is not an aggregate, as these cannot have private members. As long as i is public, however, A is an aggregate1, and no constructor is invoked since aggregate initialization is performed (see blue box), so your constructor being explicit is irrelevant.

enter image description here

However, as soon as you introduce the private member, you necessitate value-initialization as per the red box. Hence [dcl.init]/(8.2) applies:

enter image description here

[dcl.init]/(7.1) defines default-initialization for this case:

enter image description here

And §13.3.1.3 gives

For […] default-initialization, the candidate functions are all the constructors of the class of the object being initialized.

At no point is the original context - copy- or direct-initialization - considered. (§13.3.1.7 doesn't apply either.) In fact, this is intended; see CWG #1518:

This issue is resolved by the resolution of issue 1630: default initialization now uses 13.3.1.3 [over.match.ctor], which now permits explicit constructors for default-initialization.

Clang and GCC (and VC++) haven't implemented the corresponding DR yet and are thus incorrect in rejecting the code in C++14 mode.


1) Your class has a user-declared constructor, but it isn't user-provided, i.e. not impeding your class from being an aggregate. Recall the definition in [dcl.init.aggr]/1:

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).

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • 2
    Isn't [CWG 1518](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1518) relevant to this case? – bogdan Nov 04 '15 at 14:49
  • @bogdan Wait, so was my initial conclusion correct? – Columbo Nov 04 '15 at 14:51
  • I'd say so, but I'm not 100% sure myself :-). – bogdan Nov 04 '15 at 14:52
  • @bogdan Anyway, thanks a lot for that DR. I completely missed looking those up. Will now adjust my answer. – Columbo Nov 04 '15 at 14:53
  • @bogdan So what exactly does "this section" mean? How is 13.3.1 more of a "section" than 13.3.1.7? Or is it just me, and it's unambiguous in context? – Columbo Nov 04 '15 at 22:04
  • 13.3.1 is, obviously, 13.3.1.7's *innermost enclosing* section... sorry, couldn't resist :-). Every time I looked at it, I read it as "this section" == 13.3.1.7. I don't think it would make much sense for it to refer to 13.3.1. After all, that's the entire "candidate functions" section - what other section could 8.5.4 refer to? If it were 13.3.1, mentioning it would be redundant. In short, I agree with the current version of your excellent answer. – bogdan Nov 05 '15 at 00:01
  • Another confusing thing is that the same paragraph contains *If the initializer list has no elements and T has a default constructor [...]*. I think that's just a leftover from C++11, when the list in 8.5.4 was quite different. As it stands now, I don't see how you could ever get to 13.3.1.7 with an empty initializer list and a default constructor. I'd say that sentence should be removed. – bogdan Nov 05 '15 at 00:02
  • @bogdan That is *exactly* why I rectified my answer! That paragraph is bogus and should be removed; Should I open the DR? – Columbo Nov 05 '15 at 00:03
  • Yeah, I say go for it. – bogdan Nov 05 '15 at 00:21
  • ... Aaand now it appears they're changing their minds (again). Look at the latest notes for [CWG 1518](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1518). I think we'll have to wait a bit longer for a final answer. – bogdan Jan 09 '16 at 21:28