Compilation error when using empty list initialization constructor in C++17












18














I ran into a weird problem when trying to move to C++17. The problem is that something (and I'm not sure what) changed in C++17 that made list-initialization work differently in the case of a default constructor. I tried to search https://en.cppreference.com/w/cpp/language/list_initialization for more info, but I didn't find anything that looks relevant.



Does someone know the reason the code below compiles in C++14 but not in C++17 when calling B{} instead of B()?
(I tried it in both gcc 8.2 and 7.3 and icc 19)



struct A{
protected:
A() {}
};

struct B : public A {};


B f(){
return B(); //compilation OK
//return B{}; //compilation error
}









share|improve this question









New contributor




RyArazi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
















  • 1




    Try adding this flag: -std=c++0x. I am not sure though.
    – DimChtz
    2 days ago






  • 1




    @DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
    – Lightness Races in Orbit
    2 days ago












  • What are you expecting A to be? Doesn't look valid to me. {} is an initializer or scope wrapper, () is use to wrap parameters and parentheses. In your case A() is a constructor with nothing to initialise. Your compilation error is correct, return B() returns an object with nothing in it, whilst calling the empty constructor first.
    – SPlatten
    2 days ago








  • 1




    @SPlatten Well, C++ isn't an OO language, so that's reasonable :)
    – Lightness Races in Orbit
    2 days ago






  • 3




    @SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
    – Lightness Races in Orbit
    2 days ago


















18














I ran into a weird problem when trying to move to C++17. The problem is that something (and I'm not sure what) changed in C++17 that made list-initialization work differently in the case of a default constructor. I tried to search https://en.cppreference.com/w/cpp/language/list_initialization for more info, but I didn't find anything that looks relevant.



Does someone know the reason the code below compiles in C++14 but not in C++17 when calling B{} instead of B()?
(I tried it in both gcc 8.2 and 7.3 and icc 19)



struct A{
protected:
A() {}
};

struct B : public A {};


B f(){
return B(); //compilation OK
//return B{}; //compilation error
}









share|improve this question









New contributor




RyArazi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
















  • 1




    Try adding this flag: -std=c++0x. I am not sure though.
    – DimChtz
    2 days ago






  • 1




    @DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
    – Lightness Races in Orbit
    2 days ago












  • What are you expecting A to be? Doesn't look valid to me. {} is an initializer or scope wrapper, () is use to wrap parameters and parentheses. In your case A() is a constructor with nothing to initialise. Your compilation error is correct, return B() returns an object with nothing in it, whilst calling the empty constructor first.
    – SPlatten
    2 days ago








  • 1




    @SPlatten Well, C++ isn't an OO language, so that's reasonable :)
    – Lightness Races in Orbit
    2 days ago






  • 3




    @SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
    – Lightness Races in Orbit
    2 days ago
















18












18








18


2





I ran into a weird problem when trying to move to C++17. The problem is that something (and I'm not sure what) changed in C++17 that made list-initialization work differently in the case of a default constructor. I tried to search https://en.cppreference.com/w/cpp/language/list_initialization for more info, but I didn't find anything that looks relevant.



Does someone know the reason the code below compiles in C++14 but not in C++17 when calling B{} instead of B()?
(I tried it in both gcc 8.2 and 7.3 and icc 19)



struct A{
protected:
A() {}
};

struct B : public A {};


B f(){
return B(); //compilation OK
//return B{}; //compilation error
}









share|improve this question









New contributor




RyArazi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











I ran into a weird problem when trying to move to C++17. The problem is that something (and I'm not sure what) changed in C++17 that made list-initialization work differently in the case of a default constructor. I tried to search https://en.cppreference.com/w/cpp/language/list_initialization for more info, but I didn't find anything that looks relevant.



Does someone know the reason the code below compiles in C++14 but not in C++17 when calling B{} instead of B()?
(I tried it in both gcc 8.2 and 7.3 and icc 19)



struct A{
protected:
A() {}
};

struct B : public A {};


B f(){
return B(); //compilation OK
//return B{}; //compilation error
}






c++ c++17 list-initialization






share|improve this question









New contributor




RyArazi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




RyArazi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited yesterday









TylerH

15.4k105067




15.4k105067






New contributor




RyArazi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 2 days ago









RyAraziRyArazi

967




967




New contributor




RyArazi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





RyArazi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






RyArazi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.








  • 1




    Try adding this flag: -std=c++0x. I am not sure though.
    – DimChtz
    2 days ago






  • 1




    @DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
    – Lightness Races in Orbit
    2 days ago












  • What are you expecting A to be? Doesn't look valid to me. {} is an initializer or scope wrapper, () is use to wrap parameters and parentheses. In your case A() is a constructor with nothing to initialise. Your compilation error is correct, return B() returns an object with nothing in it, whilst calling the empty constructor first.
    – SPlatten
    2 days ago








  • 1




    @SPlatten Well, C++ isn't an OO language, so that's reasonable :)
    – Lightness Races in Orbit
    2 days ago






  • 3




    @SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
    – Lightness Races in Orbit
    2 days ago
















  • 1




    Try adding this flag: -std=c++0x. I am not sure though.
    – DimChtz
    2 days ago






  • 1




    @DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
    – Lightness Races in Orbit
    2 days ago












  • What are you expecting A to be? Doesn't look valid to me. {} is an initializer or scope wrapper, () is use to wrap parameters and parentheses. In your case A() is a constructor with nothing to initialise. Your compilation error is correct, return B() returns an object with nothing in it, whilst calling the empty constructor first.
    – SPlatten
    2 days ago








  • 1




    @SPlatten Well, C++ isn't an OO language, so that's reasonable :)
    – Lightness Races in Orbit
    2 days ago






  • 3




    @SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
    – Lightness Races in Orbit
    2 days ago










1




1




Try adding this flag: -std=c++0x. I am not sure though.
– DimChtz
2 days ago




Try adding this flag: -std=c++0x. I am not sure though.
– DimChtz
2 days ago




1




1




@DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
– Lightness Races in Orbit
2 days ago






@DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
– Lightness Races in Orbit
2 days ago














What are you expecting A to be? Doesn't look valid to me. {} is an initializer or scope wrapper, () is use to wrap parameters and parentheses. In your case A() is a constructor with nothing to initialise. Your compilation error is correct, return B() returns an object with nothing in it, whilst calling the empty constructor first.
– SPlatten
2 days ago






What are you expecting A to be? Doesn't look valid to me. {} is an initializer or scope wrapper, () is use to wrap parameters and parentheses. In your case A() is a constructor with nothing to initialise. Your compilation error is correct, return B() returns an object with nothing in it, whilst calling the empty constructor first.
– SPlatten
2 days ago






1




1




@SPlatten Well, C++ isn't an OO language, so that's reasonable :)
– Lightness Races in Orbit
2 days ago




@SPlatten Well, C++ isn't an OO language, so that's reasonable :)
– Lightness Races in Orbit
2 days ago




3




3




@SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
– Lightness Races in Orbit
2 days ago






@SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
– Lightness Races in Orbit
2 days ago














3 Answers
3






active

oldest

votes


















27














In C++14, the definition of aggregate was:




An aggregate is an array or a class (Clause [class]) with no user-provided constructors ([class.ctor]), no private or protected non-static data members (Clause [class.access]), no base classes (Clause [class.derived]), and no virtual functions ([class.virtual]).




Hence, B is not an aggregate. As a result B{} is surely not aggregate initialization, and B{} and B() end up meaning the same thing. They both just invoke B's default constructor.



However, in C++17, the definition of aggregate was changed to:




An aggregate is an array or a class with




  • no user-provided, explicit, or inherited constructors ([class.ctor]),

  • no private or protected non-static data members (Clause [class.access]),

  • no virtual functions, and


  • no virtual, private, or protected base classes ([class.mi]).


[ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors.  — end note ]




The restriction is no longer on any base classes, but just on virtual/private/protected ones. But B has a public base class. It is now an aggregate! And C++17 aggregate initialization does allow for initializing base class subobjects.



In particular, B{} is aggregate initialization where we just don't provide an initializer for any subobject. But the first (and only) subobject is an A, which we're trying to initialize from {} (during aggregate initialization, any subobject without an explicit initializer is copy-initialized from {}), which we can't do because A's constructor is protected and we are not a friend (see also, the quoted note).





Note that, just for fun, in C++20 the definition of aggregate will change again.






share|improve this answer

















  • 1




    @LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class with B{A{}}, and if such initializer is missing, an "empty initializer" is added for the A subobject, and not one for each members of A, as I would have thought before.
    – Holt
    2 days ago












  • @Holt Yep agreed
    – Lightness Races in Orbit
    2 days ago



















4














From my understanding of https://en.cppreference.com/w/cpp/language/value_initialization



B{} does an aggregate_initialization,



and since C++17:




The effects of aggregate initialization are:




  • Each direct public base, (since C++17) [..] is copy-initialized from the corresponding clause of the initializer list.




and in our case:




If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.




So B{/*constructor of A*/} need to construct base class A, which is protected...






share|improve this answer





























    4














    The final draft of C++17 n4659 has a compatibility section which contains the changes with respect to previous versions.




    C.4.4 Clause 11: declarators [diff.cpp14.decl]



    11.6.1

    Change: Definition of an aggregate is extended to apply to user-defined types with base classes.

    Rationale: To increase
    convenience of aggregate initialization.

    Effect on original feature:
    Valid C++ 2014 code may fail to compile or produce different results
    in this International Standard; initialization from an empty
    initializer list will perform aggregate initialization instead of
    invoking a default constructor for the affected types:



    struct derived;
    struct base {
    friend struct derived;
    private:
    base();
    };
    struct derived : base {};
    derived d1{}; // Error. The code was well-formed before.
    derived d2; // still OK



    I compiled the above example code with -std=c++14 and it compiled but failed to compile with -std=c++17.



    I believe that could be the reason why the code in the OP fails with B{} but succeeds with B().






    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });






      RyArazi is a new contributor. Be nice, and check out our Code of Conduct.










      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54092781%2fcompilation-error-when-using-empty-list-initialization-constructor-in-c17%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      27














      In C++14, the definition of aggregate was:




      An aggregate is an array or a class (Clause [class]) with no user-provided constructors ([class.ctor]), no private or protected non-static data members (Clause [class.access]), no base classes (Clause [class.derived]), and no virtual functions ([class.virtual]).




      Hence, B is not an aggregate. As a result B{} is surely not aggregate initialization, and B{} and B() end up meaning the same thing. They both just invoke B's default constructor.



      However, in C++17, the definition of aggregate was changed to:




      An aggregate is an array or a class with




      • no user-provided, explicit, or inherited constructors ([class.ctor]),

      • no private or protected non-static data members (Clause [class.access]),

      • no virtual functions, and


      • no virtual, private, or protected base classes ([class.mi]).


      [ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors.  — end note ]




      The restriction is no longer on any base classes, but just on virtual/private/protected ones. But B has a public base class. It is now an aggregate! And C++17 aggregate initialization does allow for initializing base class subobjects.



      In particular, B{} is aggregate initialization where we just don't provide an initializer for any subobject. But the first (and only) subobject is an A, which we're trying to initialize from {} (during aggregate initialization, any subobject without an explicit initializer is copy-initialized from {}), which we can't do because A's constructor is protected and we are not a friend (see also, the quoted note).





      Note that, just for fun, in C++20 the definition of aggregate will change again.






      share|improve this answer

















      • 1




        @LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class with B{A{}}, and if such initializer is missing, an "empty initializer" is added for the A subobject, and not one for each members of A, as I would have thought before.
        – Holt
        2 days ago












      • @Holt Yep agreed
        – Lightness Races in Orbit
        2 days ago
















      27














      In C++14, the definition of aggregate was:




      An aggregate is an array or a class (Clause [class]) with no user-provided constructors ([class.ctor]), no private or protected non-static data members (Clause [class.access]), no base classes (Clause [class.derived]), and no virtual functions ([class.virtual]).




      Hence, B is not an aggregate. As a result B{} is surely not aggregate initialization, and B{} and B() end up meaning the same thing. They both just invoke B's default constructor.



      However, in C++17, the definition of aggregate was changed to:




      An aggregate is an array or a class with




      • no user-provided, explicit, or inherited constructors ([class.ctor]),

      • no private or protected non-static data members (Clause [class.access]),

      • no virtual functions, and


      • no virtual, private, or protected base classes ([class.mi]).


      [ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors.  — end note ]




      The restriction is no longer on any base classes, but just on virtual/private/protected ones. But B has a public base class. It is now an aggregate! And C++17 aggregate initialization does allow for initializing base class subobjects.



      In particular, B{} is aggregate initialization where we just don't provide an initializer for any subobject. But the first (and only) subobject is an A, which we're trying to initialize from {} (during aggregate initialization, any subobject without an explicit initializer is copy-initialized from {}), which we can't do because A's constructor is protected and we are not a friend (see also, the quoted note).





      Note that, just for fun, in C++20 the definition of aggregate will change again.






      share|improve this answer

















      • 1




        @LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class with B{A{}}, and if such initializer is missing, an "empty initializer" is added for the A subobject, and not one for each members of A, as I would have thought before.
        – Holt
        2 days ago












      • @Holt Yep agreed
        – Lightness Races in Orbit
        2 days ago














      27












      27








      27






      In C++14, the definition of aggregate was:




      An aggregate is an array or a class (Clause [class]) with no user-provided constructors ([class.ctor]), no private or protected non-static data members (Clause [class.access]), no base classes (Clause [class.derived]), and no virtual functions ([class.virtual]).




      Hence, B is not an aggregate. As a result B{} is surely not aggregate initialization, and B{} and B() end up meaning the same thing. They both just invoke B's default constructor.



      However, in C++17, the definition of aggregate was changed to:




      An aggregate is an array or a class with




      • no user-provided, explicit, or inherited constructors ([class.ctor]),

      • no private or protected non-static data members (Clause [class.access]),

      • no virtual functions, and


      • no virtual, private, or protected base classes ([class.mi]).


      [ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors.  — end note ]




      The restriction is no longer on any base classes, but just on virtual/private/protected ones. But B has a public base class. It is now an aggregate! And C++17 aggregate initialization does allow for initializing base class subobjects.



      In particular, B{} is aggregate initialization where we just don't provide an initializer for any subobject. But the first (and only) subobject is an A, which we're trying to initialize from {} (during aggregate initialization, any subobject without an explicit initializer is copy-initialized from {}), which we can't do because A's constructor is protected and we are not a friend (see also, the quoted note).





      Note that, just for fun, in C++20 the definition of aggregate will change again.






      share|improve this answer












      In C++14, the definition of aggregate was:




      An aggregate is an array or a class (Clause [class]) with no user-provided constructors ([class.ctor]), no private or protected non-static data members (Clause [class.access]), no base classes (Clause [class.derived]), and no virtual functions ([class.virtual]).




      Hence, B is not an aggregate. As a result B{} is surely not aggregate initialization, and B{} and B() end up meaning the same thing. They both just invoke B's default constructor.



      However, in C++17, the definition of aggregate was changed to:




      An aggregate is an array or a class with




      • no user-provided, explicit, or inherited constructors ([class.ctor]),

      • no private or protected non-static data members (Clause [class.access]),

      • no virtual functions, and


      • no virtual, private, or protected base classes ([class.mi]).


      [ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors.  — end note ]




      The restriction is no longer on any base classes, but just on virtual/private/protected ones. But B has a public base class. It is now an aggregate! And C++17 aggregate initialization does allow for initializing base class subobjects.



      In particular, B{} is aggregate initialization where we just don't provide an initializer for any subobject. But the first (and only) subobject is an A, which we're trying to initialize from {} (during aggregate initialization, any subobject without an explicit initializer is copy-initialized from {}), which we can't do because A's constructor is protected and we are not a friend (see also, the quoted note).





      Note that, just for fun, in C++20 the definition of aggregate will change again.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered 2 days ago









      BarryBarry

      178k18308565




      178k18308565








      • 1




        @LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class with B{A{}}, and if such initializer is missing, an "empty initializer" is added for the A subobject, and not one for each members of A, as I would have thought before.
        – Holt
        2 days ago












      • @Holt Yep agreed
        – Lightness Races in Orbit
        2 days ago














      • 1




        @LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class with B{A{}}, and if such initializer is missing, an "empty initializer" is added for the A subobject, and not one for each members of A, as I would have thought before.
        – Holt
        2 days ago












      • @Holt Yep agreed
        – Lightness Races in Orbit
        2 days ago








      1




      1




      @LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class with B{A{}}, and if such initializer is missing, an "empty initializer" is added for the A subobject, and not one for each members of A, as I would have thought before.
      – Holt
      2 days ago






      @LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class with B{A{}}, and if such initializer is missing, an "empty initializer" is added for the A subobject, and not one for each members of A, as I would have thought before.
      – Holt
      2 days ago














      @Holt Yep agreed
      – Lightness Races in Orbit
      2 days ago




      @Holt Yep agreed
      – Lightness Races in Orbit
      2 days ago













      4














      From my understanding of https://en.cppreference.com/w/cpp/language/value_initialization



      B{} does an aggregate_initialization,



      and since C++17:




      The effects of aggregate initialization are:




      • Each direct public base, (since C++17) [..] is copy-initialized from the corresponding clause of the initializer list.




      and in our case:




      If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.




      So B{/*constructor of A*/} need to construct base class A, which is protected...






      share|improve this answer


























        4














        From my understanding of https://en.cppreference.com/w/cpp/language/value_initialization



        B{} does an aggregate_initialization,



        and since C++17:




        The effects of aggregate initialization are:




        • Each direct public base, (since C++17) [..] is copy-initialized from the corresponding clause of the initializer list.




        and in our case:




        If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.




        So B{/*constructor of A*/} need to construct base class A, which is protected...






        share|improve this answer
























          4












          4








          4






          From my understanding of https://en.cppreference.com/w/cpp/language/value_initialization



          B{} does an aggregate_initialization,



          and since C++17:




          The effects of aggregate initialization are:




          • Each direct public base, (since C++17) [..] is copy-initialized from the corresponding clause of the initializer list.




          and in our case:




          If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.




          So B{/*constructor of A*/} need to construct base class A, which is protected...






          share|improve this answer












          From my understanding of https://en.cppreference.com/w/cpp/language/value_initialization



          B{} does an aggregate_initialization,



          and since C++17:




          The effects of aggregate initialization are:




          • Each direct public base, (since C++17) [..] is copy-initialized from the corresponding clause of the initializer list.




          and in our case:




          If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.




          So B{/*constructor of A*/} need to construct base class A, which is protected...







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered 2 days ago









          Jarod42Jarod42

          114k12101182




          114k12101182























              4














              The final draft of C++17 n4659 has a compatibility section which contains the changes with respect to previous versions.




              C.4.4 Clause 11: declarators [diff.cpp14.decl]



              11.6.1

              Change: Definition of an aggregate is extended to apply to user-defined types with base classes.

              Rationale: To increase
              convenience of aggregate initialization.

              Effect on original feature:
              Valid C++ 2014 code may fail to compile or produce different results
              in this International Standard; initialization from an empty
              initializer list will perform aggregate initialization instead of
              invoking a default constructor for the affected types:



              struct derived;
              struct base {
              friend struct derived;
              private:
              base();
              };
              struct derived : base {};
              derived d1{}; // Error. The code was well-formed before.
              derived d2; // still OK



              I compiled the above example code with -std=c++14 and it compiled but failed to compile with -std=c++17.



              I believe that could be the reason why the code in the OP fails with B{} but succeeds with B().






              share|improve this answer




























                4














                The final draft of C++17 n4659 has a compatibility section which contains the changes with respect to previous versions.




                C.4.4 Clause 11: declarators [diff.cpp14.decl]



                11.6.1

                Change: Definition of an aggregate is extended to apply to user-defined types with base classes.

                Rationale: To increase
                convenience of aggregate initialization.

                Effect on original feature:
                Valid C++ 2014 code may fail to compile or produce different results
                in this International Standard; initialization from an empty
                initializer list will perform aggregate initialization instead of
                invoking a default constructor for the affected types:



                struct derived;
                struct base {
                friend struct derived;
                private:
                base();
                };
                struct derived : base {};
                derived d1{}; // Error. The code was well-formed before.
                derived d2; // still OK



                I compiled the above example code with -std=c++14 and it compiled but failed to compile with -std=c++17.



                I believe that could be the reason why the code in the OP fails with B{} but succeeds with B().






                share|improve this answer


























                  4












                  4








                  4






                  The final draft of C++17 n4659 has a compatibility section which contains the changes with respect to previous versions.




                  C.4.4 Clause 11: declarators [diff.cpp14.decl]



                  11.6.1

                  Change: Definition of an aggregate is extended to apply to user-defined types with base classes.

                  Rationale: To increase
                  convenience of aggregate initialization.

                  Effect on original feature:
                  Valid C++ 2014 code may fail to compile or produce different results
                  in this International Standard; initialization from an empty
                  initializer list will perform aggregate initialization instead of
                  invoking a default constructor for the affected types:



                  struct derived;
                  struct base {
                  friend struct derived;
                  private:
                  base();
                  };
                  struct derived : base {};
                  derived d1{}; // Error. The code was well-formed before.
                  derived d2; // still OK



                  I compiled the above example code with -std=c++14 and it compiled but failed to compile with -std=c++17.



                  I believe that could be the reason why the code in the OP fails with B{} but succeeds with B().






                  share|improve this answer














                  The final draft of C++17 n4659 has a compatibility section which contains the changes with respect to previous versions.




                  C.4.4 Clause 11: declarators [diff.cpp14.decl]



                  11.6.1

                  Change: Definition of an aggregate is extended to apply to user-defined types with base classes.

                  Rationale: To increase
                  convenience of aggregate initialization.

                  Effect on original feature:
                  Valid C++ 2014 code may fail to compile or produce different results
                  in this International Standard; initialization from an empty
                  initializer list will perform aggregate initialization instead of
                  invoking a default constructor for the affected types:



                  struct derived;
                  struct base {
                  friend struct derived;
                  private:
                  base();
                  };
                  struct derived : base {};
                  derived d1{}; // Error. The code was well-formed before.
                  derived d2; // still OK



                  I compiled the above example code with -std=c++14 and it compiled but failed to compile with -std=c++17.



                  I believe that could be the reason why the code in the OP fails with B{} but succeeds with B().







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited 2 days ago

























                  answered 2 days ago









                  P.WP.W

                  11.8k3842




                  11.8k3842






















                      RyArazi is a new contributor. Be nice, and check out our Code of Conduct.










                      draft saved

                      draft discarded


















                      RyArazi is a new contributor. Be nice, and check out our Code of Conduct.













                      RyArazi is a new contributor. Be nice, and check out our Code of Conduct.












                      RyArazi is a new contributor. Be nice, and check out our Code of Conduct.
















                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54092781%2fcompilation-error-when-using-empty-list-initialization-constructor-in-c17%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      An IMO inspired problem

                      Management

                      Has there ever been an instance of an active nuclear power plant within or near a war zone?