tag:blogger.com,1999:blog-7101933101966798446.post7455783532878848519..comments2024-03-28T10:33:06.910-07:00Comments on The View from Aristeia: Help me sort out the meaning of "{}" as a constructor argumentScott Meyershttp://www.blogger.com/profile/05280964633768289328noreply@blogger.comBlogger17125tag:blogger.com,1999:blog-7101933101966798446.post-40350708572155074052016-11-25T09:13:12.928-08:002016-11-25T09:13:12.928-08:00I've now updated the blog post with what I bel...I've now updated the blog post with what I believe to be the resolution to the issues I originally raised. Thanks to everybody for their help.Scott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-47645369682423175432016-11-22T17:06:21.896-08:002016-11-22T17:06:21.896-08:00@T. C., @Marco Alesiani, @Anonymous: Thanks to you...@T. C., @Marco Alesiani, @Anonymous: Thanks to your comments and to the thread on reddit, I think I now understand why<br /><br /><br />X b0{{}};<br /><br /><br />yields a one-element std::initializer_list. You can read my reddit post <a href="https://www.reddit.com/r/cpp/comments/5e8oq6/scott_meyers_needs_help/dabshu4/" rel="nofollow">here</a>.Scott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-35243068460103577792016-11-22T11:07:48.895-08:002016-11-22T11:07:48.895-08:00This has bugged me for a long time with linux MAN...This has bugged me for a long time with linux MAN pages and OSX too. <br /><br />I'm more confused after this article.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-33116103032513476372016-11-22T10:47:31.852-08:002016-11-22T10:47:31.852-08:00Hi,
I'm not the author of the answer from the...Hi,<br /><br />I'm not the author of the answer from the reddit/r/cpp, just posting it for you:<br /><br />https://www.reddit.com/r/cpp/comments/5e8oq6/scott_meyers_needs_help/daatihr/<br /><br />ThanksAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-53765177472553684242016-11-22T10:44:27.727-08:002016-11-22T10:44:27.727-08:00I've updated the post to include revised code ...I've updated the post to include revised code that prevents aggregate initialization. Thanks to everybody for pointing out that my original definition of DeletedDefCtor made it an aggregate.Scott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-78159887052738955882016-11-22T05:50:10.162-08:002016-11-22T05:50:10.162-08:00Welcome back, Scott!
The interesting thing here ...Welcome back, Scott!<br /> <br />The interesting thing here is that DeletedDefCtor is still an aggregate because it doesn't have a user-provided constructor (http://eel.is/c++draft/dcl.fct.def.default#5, you explicitly deleted it but that doesn't count). So DeletedDefCtor{} is still well-formed.<br /> <br />The different takes on the explicit constructor are resolved by CWG Issue 1518 (https://wg21.link/p0398) - now DefCtor with an explicit constructor is not an aggregate, so trying to initialize it with {} would be ill-formed. Anonymoushttps://www.blogger.com/profile/03513030298913327791noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-85262357965497203092016-11-22T05:24:40.873-08:002016-11-22T05:24:40.873-08:00In all cases, it's the initializer_list constr...In all cases, it's the initializer_list constructor that's used, but if possible that comes by viewing the construction expression as a list expression (elements can be constructed with the inner {}), otherwise, if that fails, the initializer_list constructor is used coupled to direct initialization, and the outer brace becomes like a parenthesis.Anonymoushttps://www.blogger.com/profile/11653390099233773707noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-52172428952297575462016-11-22T05:05:04.520-08:002016-11-22T05:05:04.520-08:00"In your answer to question 2, you mention st..."In your answer to question 2, you mention struct initialization, by which I assume you mean aggregate initialization. But there are no aggregates (e.g., no structs) in the example. Everything is a class."<br /><br />Hi Scott, yes I mean that. Using the class keyword won't prevent you from creating a POD, class Foo {public:} and struct Foo {} is essentialy the same thing as far as I know, and aggregate initialization is allowed in all those cases, that's why I said DeletedDefCtor{} still works.<br /><br />"There's no issue of the std::initializer_list constructor taking precedence in some cases here, because the std::initializer_list constructor is called in every case."<br /><br />Correct, 3rd message about precedence should not be following well my intuition. The "precedence" here regards with interpreting the outer brace as a list expression should take precedence over interpreting it as direct initialization.Anonymoushttps://www.blogger.com/profile/11653390099233773707noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-24155262683430511032016-11-22T03:46:33.205-08:002016-11-22T03:46:33.205-08:00Aggregate initialization is allowed in the Deleted...Aggregate initialization is allowed in the DeletedDefCtor case. http://en.cppreference.com/w/cpp/language/aggregate_initializationJeznoreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-5894828538395584732016-11-22T02:01:50.071-08:002016-11-22T02:01:50.071-08:00I'm not completely sure but here's my atte...I'm not completely sure but here's my attempt (please correct me if I'm wrong):<br /><br />- with DefCtor [dcl.init.list]/p5 holds and everything goes smooth<br />- with NoDefCtor no temporary can be created and [over.match.list]/p1 kicks in<br /><br />Just my guess though.Anonymoushttps://www.blogger.com/profile/03144538093086954078noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-44386013115261638532016-11-22T01:14:05.578-08:002016-11-22T01:14:05.578-08:00For some reason, the compilers identify DeletedDef...For some reason, the compilers identify DeletedDefCtor as an aggregate. You can try, for example:<br /><br /> class DeletedDefCtor {<br /> public:<br /> DeletedDefCtor() = delete;<br /> int i;<br /> int j;<br /> };<br /><br /> DeletedDefCtor x{0, 1};<br /><br />This still compiles on gcc and clang. Regarding the Standard, I think the deleted ctor is user-provided and hence this class should *not* be an aggregate.<br /><br />Using {} to initialize an aggregate does not call the default ctor, which is why DeletedDefCtor{} works, and probably also why void foo(DeletedDefCtor); foo({}) works.<br /><br />Regarding explicit ctors, please see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1518<br />Also note that explicitness does not affect overload resolution for conversion by brace-initialization, which was supposed to prevent errors. Consider:<br /><br /> struct Foo<br /> {<br /> Foo(short);<br /> explicit Foo(int) {}<br /> };<br /> <br /> void foo(Foo) {}<br /> <br /> int main()<br /> {<br /> Foo f = 42; // OK, using Foo(short)<br /> Foo g{42}; // OK, using explicit Foo(int)<br /> Foo h = {42}; // error: trying to use explicit Foo(int)<br /> foo({42}); // error: trying to use explicit Foo(int)<br /> }Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-66628635587803998482016-11-22T00:12:05.567-08:002016-11-22T00:12:05.567-08:00This is a consequence of the "initializer_lis...This is a consequence of the "initializer_list if at all possible" rule. That is to say, the rule is that if {} can be interpreted as initializing a T, it is.<br /><br />1. if there is a default constructor, then {} is interpreted as an initializer for T and it calls the initializer-list constructor with an list containing one element. If {} cannot be so interpreted, then the {{}} initialization behaves like ({}).<br />2. Because DeletedDefCtor is an aggregate and can be initialized from {} by aggregate initialization. If you give it a private data member it would behave like the "NoDefCtor" case.<br />3. I believe that the intent here is in line with GCC's behavior, i.e., ExplicitDefCtor is considered to be constructible from {} for overload resolution purposes, so the compiler goes with the initializer-list interpretation only to reject the program for attempting to use an explicit constructor in copy-list-initialization.<br /><br />Explicit default constructors are also subject to a couple recent defect reports, resolved by http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0398r0.html.T. C.noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-10362567413904557902016-11-21T20:58:57.147-08:002016-11-21T20:58:57.147-08:00Ouu, Ouu, I don't have a solution, but I have ...Ouu, Ouu, I don't have a solution, but I have a name for this: The most curly parsematthewaveryusahttps://www.blogger.com/profile/08745072897111164448noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-91490133793792152942016-11-21T20:28:18.512-08:002016-11-21T20:28:18.512-08:00@Francisco Lopes: I think we may be miscommunicati...@Francisco Lopes: I think we may be miscommunicating a bit. In main, every object constructed is constructed using the std::initializer_list constructor in at least some way. ("Def Ctor" is never printed, so we know the default constructor is never called. Whether move or copy constructors are involved is not clear; I'm hoping not.) There's no issue of the std::initializer_list constructor taking precedence in some cases here, because the std::initializer_list constructor is called in every case.<br /><br />The classes DefCtor, DeletedDefCtor, and NoDefCtor all lack std::initialzer_list constructors, so for objects of those types, a std::initializer_list constructor is never called.<br /><br />In your answer to question 2, you mention struct initialization, by which I assume you mean aggregate initialization. But there are no aggregates (e.g., no structs) in the example. Everything is a class. In those cases where one-element std::initializer_list objects are created, I'm guessing that objects are being default-constructed by interpreting "{}" as an empty parameter list to a T constructor instead of as an empty std::initializer_list<T>, but why?Scott Meyershttps://www.blogger.com/profile/05280964633768289328noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-32681103079538302522016-11-21T18:30:34.641-08:002016-11-21T18:30:34.641-08:00And I guess all of that can be summed up with the ...And I guess all of that can be summed up with the initializer_list constructor just taking precedence in overload resolution when list initialization is used, but not eliminating other forms of construction from the resolution.Anonymoushttps://www.blogger.com/profile/11653390099233773707noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-11566004432470978472016-11-21T17:01:07.235-08:002016-11-21T17:01:07.235-08:00So, trying to answering the questions just buildin...So, trying to answering the questions just building on the previous intuition:<br /><br />1 - Because when there's no default constructor, there's no way for construction to work favoring list semantics, so it fallback to standard construction, like if using parenthesis.<br /><br />2 - deleted default construct doesn't prevent from struct initialization. Notice how DeletedDefCtor{} still works. So, this is still used if possible. If you really want not only default construction but also struct initialization you may try adding virtual ~DeletedDefCtor(){} to the DeletedDefCtor class. You'll see that now that b2{{}} won't work anymore. In this case the fallback from list semantics construction to normal construction is not happening.<br /><br />3 - Unable to answer this one from basic intuition but you may check that b0{DefCtor{}} is accepted by GCC, so it seems the question boils down to whether {} alone is an explicit enough construction expression, clang seems to think so, GCC doesn't.<br /><br />4 - This can be worked out from another "fallback" intuition. Notice how in the explicit case, b0{DefCtor{}} builds a 1-sized list (because that's can be done/interpreted), b0{{}} is a 0-sized list because I'm guessing {} is not an explicit enough construction expression like DefCtor{}, and since it's not, then it's not initializing the element which requires so, so if the inner {} is left to initialize the initializer_list argument, then the outer {} is just not tied to that argument but just direct initialization.<br /><br />Sorry if it's crazy enough inference, it should look like built up to explain.<br /><br />Sometimes that's what I do to keep moving on: put some sense where there's none :-)Anonymoushttps://www.blogger.com/profile/11653390099233773707noreply@blogger.comtag:blogger.com,1999:blog-7101933101966798446.post-88782367686679878532016-11-21T16:17:36.533-08:002016-11-21T16:17:36.533-08:00I'm building an intuition (didn't check st...I'm building an intuition (didn't check standard wording) that outer braces (list-initialization) is favoring list semantics if possible. From this intuition the behavior follows naturally:<br /><br />- a0({}) -> () is used to construct with an empty list {}<br />- b0{{}} -> outer {} is viewed in list semantics b/c of the initializer_list constructor, so this outer {} is itself tied to the initializer_list, the inner {} initializes an element, and it's all as if it was sugar for b0({{}}). Notice that hence, b0{{}, {}}, etc also works.<br />- DeletedDefCtor{} -> this is still valid, meaning, removing default constructor doesn't prevent a0/b0 behaviour.<br />- b1{{}} -> This would fail construction if it favored list semantics like in b0{{}}, so the next option is not assume outer {} as tied to the initializer_list argument, but just equivalent to (), meaning that the expression is equivalent to b1({}), since that's what can work.Anonymoushttps://www.blogger.com/profile/11653390099233773707noreply@blogger.com