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












20















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




















  • 1





    Try adding this flag: -std=c++0x. I am not sure though.

    – DimChtz
    Jan 8 at 13:26






  • 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
    Jan 8 at 13:27








  • 1





    @SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keyword struct can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's type A here.

    – Lightness Races in Orbit
    Jan 8 at 14:24








  • 1





    @SPlatten Well, C++ isn't an OO language, so that's reasonable :)

    – Lightness Races in Orbit
    Jan 8 at 14:26






  • 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
    Jan 8 at 14:29


















20















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




















  • 1





    Try adding this flag: -std=c++0x. I am not sure though.

    – DimChtz
    Jan 8 at 13:26






  • 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
    Jan 8 at 13:27








  • 1





    @SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keyword struct can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's type A here.

    – Lightness Races in Orbit
    Jan 8 at 14:24








  • 1





    @SPlatten Well, C++ isn't an OO language, so that's reasonable :)

    – Lightness Races in Orbit
    Jan 8 at 14:26






  • 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
    Jan 8 at 14:29
















20












20








20


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
















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















share|improve this question













share|improve this question




share|improve this question








edited Jan 8 at 16:33









TylerH

15.5k105067




15.5k105067










asked Jan 8 at 13:23









RyAraziRyArazi

1067




1067








  • 1





    Try adding this flag: -std=c++0x. I am not sure though.

    – DimChtz
    Jan 8 at 13:26






  • 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
    Jan 8 at 13:27








  • 1





    @SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keyword struct can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's type A here.

    – Lightness Races in Orbit
    Jan 8 at 14:24








  • 1





    @SPlatten Well, C++ isn't an OO language, so that's reasonable :)

    – Lightness Races in Orbit
    Jan 8 at 14:26






  • 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
    Jan 8 at 14:29
















  • 1





    Try adding this flag: -std=c++0x. I am not sure though.

    – DimChtz
    Jan 8 at 13:26






  • 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
    Jan 8 at 13:27








  • 1





    @SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keyword struct can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's type A here.

    – Lightness Races in Orbit
    Jan 8 at 14:24








  • 1





    @SPlatten Well, C++ isn't an OO language, so that's reasonable :)

    – Lightness Races in Orbit
    Jan 8 at 14:26






  • 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
    Jan 8 at 14:29










1




1





Try adding this flag: -std=c++0x. I am not sure though.

– DimChtz
Jan 8 at 13:26





Try adding this flag: -std=c++0x. I am not sure though.

– DimChtz
Jan 8 at 13:26




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
Jan 8 at 13:27







@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
Jan 8 at 13:27






1




1





@SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keyword struct can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's type A here.

– Lightness Races in Orbit
Jan 8 at 14:24







@SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keyword struct can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's type A here.

– Lightness Races in Orbit
Jan 8 at 14:24






1




1





@SPlatten Well, C++ isn't an OO language, so that's reasonable :)

– Lightness Races in Orbit
Jan 8 at 14:26





@SPlatten Well, C++ isn't an OO language, so that's reasonable :)

– Lightness Races in Orbit
Jan 8 at 14:26




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
Jan 8 at 14:29







@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
Jan 8 at 14:29














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
    Jan 8 at 14:16













  • @Holt Yep agreed

    – Lightness Races in Orbit
    Jan 8 at 14:31



















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
      });


      }
      });














      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
        Jan 8 at 14:16













      • @Holt Yep agreed

        – Lightness Races in Orbit
        Jan 8 at 14:31
















      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
        Jan 8 at 14:16













      • @Holt Yep agreed

        – Lightness Races in Orbit
        Jan 8 at 14:31














      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 Jan 8 at 14:01









      BarryBarry

      179k18310568




      179k18310568








      • 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
        Jan 8 at 14:16













      • @Holt Yep agreed

        – Lightness Races in Orbit
        Jan 8 at 14:31














      • 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
        Jan 8 at 14:16













      • @Holt Yep agreed

        – Lightness Races in Orbit
        Jan 8 at 14:31








      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
      Jan 8 at 14:16







      @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
      Jan 8 at 14:16















      @Holt Yep agreed

      – Lightness Races in Orbit
      Jan 8 at 14:31





      @Holt Yep agreed

      – Lightness Races in Orbit
      Jan 8 at 14:31













      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 Jan 8 at 14:01









          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 Jan 8 at 15:25

























                  answered Jan 8 at 14:07









                  P.WP.W

                  12.2k3843




                  12.2k3843






























                      draft saved

                      draft discarded




















































                      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

                      Mario Kart Wii

                      What does “Dominus providebit” mean?

                      Antonio Litta Visconti Arese