Why is an initializer_list of enum values not considered a constant expression?












11















In the following code (tested locally and on Wandbox):



#include <iostream>

enum Types
{
A, B, C, D
};

void print(std::initializer_list<Types> types)
{
for (auto type : types)
{
std::cout << type << std::endl;
}
}

int main()
{
constexpr auto const group1 = { A, D };
print(group1);
return 0;
}


MSVC 15.8.5 fails to compile with:



error C2131: expression did not evaluate to a constant
note: failure was caused by a read of a variable outside its lifetime
note: see usage of '$S1'


(all referring to the line containing constexpr)



Clang 8 (HEAD) reports:



error: constexpr variable 'group1' must be initialized by a constant expression
constexpr auto const group1 = { A, D };
^ ~~~~~~~~
note: pointer to subobject of temporary is not a constant expression
note: temporary created here
constexpr auto const group1 = { A, D };
^


gcc 9 (HEAD) reports:



In function 'int main()':
error: 'const std::initializer_list<const Types>{((const Types*)(&<anonymous>)), 2}' is not a constant expression
18 | constexpr auto const group1 = { A, D };
| ^
error: could not convert 'group1' from 'initializer_list<const Types>' to 'initializer_list<Types>'
19 | print(group1);
| ^~~~~~
| |
| initializer_list<const Types>


Why?



Firstly, they all apparently consider enum-ids to be non-constant, despite them obviously actually being well-known compile-time constant values.



Secondly, MSVC complains about read outside lifetime, but the lifetime of group1 and its values should extend throughout its usage in print.



Thirdly, gcc has a weird const-vs-non-const complaint that I can't make any sense of, since initialiser lists are always const.



Finally, all but gcc will happily compile and run this code without any problems if constexpr is removed. Granted it is not necessary in this case, but I can't see any good reason for it not to work.



Meanwhile gcc will only compile and run the code if the parameter type is changed to std::initializer_list<const Types> -- and making this change causes it to fail to compile in both MSVC and clang.



(Interestingly: gcc 8, with the parameter type change, does successfully compile and run the code including constexpr, where gcc 9 errors out.)





FWIW, changing the declaration to this:



    constexpr auto const group1 = std::array<Types, 2>{ A, D };


Does compile and run on all three compilers. So it is probably the initializer_list itself that is misbehaving rather than the enum values. But the syntax is more annoying. (It's slightly less annoying with a suitable make_array implementation, but I still don't see why the original isn't valid.)










share|improve this question

























  • I like the question, I wonder if it is a bug (can be logged on the trackers of the compilers?). Just for curiousity, how does it behave with Enum-class?

    – JVApen
    4 hours ago
















11















In the following code (tested locally and on Wandbox):



#include <iostream>

enum Types
{
A, B, C, D
};

void print(std::initializer_list<Types> types)
{
for (auto type : types)
{
std::cout << type << std::endl;
}
}

int main()
{
constexpr auto const group1 = { A, D };
print(group1);
return 0;
}


MSVC 15.8.5 fails to compile with:



error C2131: expression did not evaluate to a constant
note: failure was caused by a read of a variable outside its lifetime
note: see usage of '$S1'


(all referring to the line containing constexpr)



Clang 8 (HEAD) reports:



error: constexpr variable 'group1' must be initialized by a constant expression
constexpr auto const group1 = { A, D };
^ ~~~~~~~~
note: pointer to subobject of temporary is not a constant expression
note: temporary created here
constexpr auto const group1 = { A, D };
^


gcc 9 (HEAD) reports:



In function 'int main()':
error: 'const std::initializer_list<const Types>{((const Types*)(&<anonymous>)), 2}' is not a constant expression
18 | constexpr auto const group1 = { A, D };
| ^
error: could not convert 'group1' from 'initializer_list<const Types>' to 'initializer_list<Types>'
19 | print(group1);
| ^~~~~~
| |
| initializer_list<const Types>


Why?



Firstly, they all apparently consider enum-ids to be non-constant, despite them obviously actually being well-known compile-time constant values.



Secondly, MSVC complains about read outside lifetime, but the lifetime of group1 and its values should extend throughout its usage in print.



Thirdly, gcc has a weird const-vs-non-const complaint that I can't make any sense of, since initialiser lists are always const.



Finally, all but gcc will happily compile and run this code without any problems if constexpr is removed. Granted it is not necessary in this case, but I can't see any good reason for it not to work.



Meanwhile gcc will only compile and run the code if the parameter type is changed to std::initializer_list<const Types> -- and making this change causes it to fail to compile in both MSVC and clang.



(Interestingly: gcc 8, with the parameter type change, does successfully compile and run the code including constexpr, where gcc 9 errors out.)





FWIW, changing the declaration to this:



    constexpr auto const group1 = std::array<Types, 2>{ A, D };


Does compile and run on all three compilers. So it is probably the initializer_list itself that is misbehaving rather than the enum values. But the syntax is more annoying. (It's slightly less annoying with a suitable make_array implementation, but I still don't see why the original isn't valid.)










share|improve this question

























  • I like the question, I wonder if it is a bug (can be logged on the trackers of the compilers?). Just for curiousity, how does it behave with Enum-class?

    – JVApen
    4 hours ago














11












11








11


2






In the following code (tested locally and on Wandbox):



#include <iostream>

enum Types
{
A, B, C, D
};

void print(std::initializer_list<Types> types)
{
for (auto type : types)
{
std::cout << type << std::endl;
}
}

int main()
{
constexpr auto const group1 = { A, D };
print(group1);
return 0;
}


MSVC 15.8.5 fails to compile with:



error C2131: expression did not evaluate to a constant
note: failure was caused by a read of a variable outside its lifetime
note: see usage of '$S1'


(all referring to the line containing constexpr)



Clang 8 (HEAD) reports:



error: constexpr variable 'group1' must be initialized by a constant expression
constexpr auto const group1 = { A, D };
^ ~~~~~~~~
note: pointer to subobject of temporary is not a constant expression
note: temporary created here
constexpr auto const group1 = { A, D };
^


gcc 9 (HEAD) reports:



In function 'int main()':
error: 'const std::initializer_list<const Types>{((const Types*)(&<anonymous>)), 2}' is not a constant expression
18 | constexpr auto const group1 = { A, D };
| ^
error: could not convert 'group1' from 'initializer_list<const Types>' to 'initializer_list<Types>'
19 | print(group1);
| ^~~~~~
| |
| initializer_list<const Types>


Why?



Firstly, they all apparently consider enum-ids to be non-constant, despite them obviously actually being well-known compile-time constant values.



Secondly, MSVC complains about read outside lifetime, but the lifetime of group1 and its values should extend throughout its usage in print.



Thirdly, gcc has a weird const-vs-non-const complaint that I can't make any sense of, since initialiser lists are always const.



Finally, all but gcc will happily compile and run this code without any problems if constexpr is removed. Granted it is not necessary in this case, but I can't see any good reason for it not to work.



Meanwhile gcc will only compile and run the code if the parameter type is changed to std::initializer_list<const Types> -- and making this change causes it to fail to compile in both MSVC and clang.



(Interestingly: gcc 8, with the parameter type change, does successfully compile and run the code including constexpr, where gcc 9 errors out.)





FWIW, changing the declaration to this:



    constexpr auto const group1 = std::array<Types, 2>{ A, D };


Does compile and run on all three compilers. So it is probably the initializer_list itself that is misbehaving rather than the enum values. But the syntax is more annoying. (It's slightly less annoying with a suitable make_array implementation, but I still don't see why the original isn't valid.)










share|improve this question
















In the following code (tested locally and on Wandbox):



#include <iostream>

enum Types
{
A, B, C, D
};

void print(std::initializer_list<Types> types)
{
for (auto type : types)
{
std::cout << type << std::endl;
}
}

int main()
{
constexpr auto const group1 = { A, D };
print(group1);
return 0;
}


MSVC 15.8.5 fails to compile with:



error C2131: expression did not evaluate to a constant
note: failure was caused by a read of a variable outside its lifetime
note: see usage of '$S1'


(all referring to the line containing constexpr)



Clang 8 (HEAD) reports:



error: constexpr variable 'group1' must be initialized by a constant expression
constexpr auto const group1 = { A, D };
^ ~~~~~~~~
note: pointer to subobject of temporary is not a constant expression
note: temporary created here
constexpr auto const group1 = { A, D };
^


gcc 9 (HEAD) reports:



In function 'int main()':
error: 'const std::initializer_list<const Types>{((const Types*)(&<anonymous>)), 2}' is not a constant expression
18 | constexpr auto const group1 = { A, D };
| ^
error: could not convert 'group1' from 'initializer_list<const Types>' to 'initializer_list<Types>'
19 | print(group1);
| ^~~~~~
| |
| initializer_list<const Types>


Why?



Firstly, they all apparently consider enum-ids to be non-constant, despite them obviously actually being well-known compile-time constant values.



Secondly, MSVC complains about read outside lifetime, but the lifetime of group1 and its values should extend throughout its usage in print.



Thirdly, gcc has a weird const-vs-non-const complaint that I can't make any sense of, since initialiser lists are always const.



Finally, all but gcc will happily compile and run this code without any problems if constexpr is removed. Granted it is not necessary in this case, but I can't see any good reason for it not to work.



Meanwhile gcc will only compile and run the code if the parameter type is changed to std::initializer_list<const Types> -- and making this change causes it to fail to compile in both MSVC and clang.



(Interestingly: gcc 8, with the parameter type change, does successfully compile and run the code including constexpr, where gcc 9 errors out.)





FWIW, changing the declaration to this:



    constexpr auto const group1 = std::array<Types, 2>{ A, D };


Does compile and run on all three compilers. So it is probably the initializer_list itself that is misbehaving rather than the enum values. But the syntax is more annoying. (It's slightly less annoying with a suitable make_array implementation, but I still don't see why the original isn't valid.)







c++ language-lawyer c++17 constexpr






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 4 hours ago









Rakete1111

34.2k980117




34.2k980117










asked 4 hours ago









MiralMiral

8,08623270




8,08623270













  • I like the question, I wonder if it is a bug (can be logged on the trackers of the compilers?). Just for curiousity, how does it behave with Enum-class?

    – JVApen
    4 hours ago



















  • I like the question, I wonder if it is a bug (can be logged on the trackers of the compilers?). Just for curiousity, how does it behave with Enum-class?

    – JVApen
    4 hours ago

















I like the question, I wonder if it is a bug (can be logged on the trackers of the compilers?). Just for curiousity, how does it behave with Enum-class?

– JVApen
4 hours ago





I like the question, I wonder if it is a bug (can be logged on the trackers of the compilers?). Just for curiousity, how does it behave with Enum-class?

– JVApen
4 hours ago












2 Answers
2






active

oldest

votes


















5














When you initialize a std::initializer_list it happens like this:




[dcl.init.list] (emphasis mine)



5 An object of type std​::​initializer_­list is constructed
from an initializer list as if the implementation generated and
materialized a prvalue of type “array of N const E”
, where N is the
number of elements in the initializer list. Each element of that array
is copy-initialized with the corresponding element of the initializer
list, and the std​::​initializer_­list object is constructed to
refer to that array. [ Note: A constructor or conversion function
selected for the copy shall be accessible in the context of the
initializer list.  — end note ] If a narrowing conversion is required
to initialize any of the elements, the program is ill-formed.
[ Example:



struct X {
X(std::initializer_list<double> v);
};
X x{ 1,2,3 };


The initialization will be implemented in a way roughly equivalent to
this:



const double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));


assuming that the implementation can construct an initializer_­list
object with a pair of pointers.  — end example ]




How that temporary array gets used to initialize the std::initializer_list is what determines if the initializer_list is initialized with a constant expression. Ultimately, according to example (despite being non-normative), that initialization is going to take the address of the array, or its first element, which will produce a value of a pointer type. And that is not a valid constant expression.




[expr.const] (emphasis mine)



5 A constant expression is either a glvalue core constant
expression that refers to an entity that is a permitted result of a
constant expression (as defined below), or a prvalue core constant
expression whose value satisfies the following constraints:




  • if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a
    constant expression,

  • if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such
    an object ([expr.add]), the address of a function, or a null pointer
    value, and

  • if the value is an object of class or array type, each subobject satisfies these constraints for the value.


An entity is a permitted result of a constant expression if it is an
object with static storage duration that is either not a temporary
object or is a temporary object whose value satisfies the above
constraints, or it is a function.




If the array was however a static object, then that initializer would constitute a valid constant expression that can be used to initialize a constexpr object. Since std::initializer_list has an effect of lifetime extension on that temporary by [dcl.init.list]/6, when you declare group1 as a static object, clang and gcc seem allocate the array as a static object too, which makes the initialization's well-formedness subject only to whether or not std::initializer_list is a literal type and the constructor being used is constexpr.



Ultimately, it's all a bit murky.






share|improve this answer

































    3














    It appears std::initializer_list does not yet (in C++17) fulfill the requirements of literal type (which is a requirement the type of a constexpr variable has to satisfy).



    A discussion on whether it does so in C++14 is found in this post: Why isn't std::initializer_list defined as a literal type?
    which itself was a followup to the post discussing Is it legal to declare a constexpr initializer_list object?



    I compared the citations provided in the C++14 related post (of the C++14 standard) to the final working draft (of the C++17 standard) and they are the same.
    So there is no explicit requirement that std::initializer_list should be a literal type.



    Citations from the final working draft of C++17 (n4659):



    [basic.types]/10.5




    (10.5) a possibly cv-qualified class type (Clause 12) that has all of the
    following properties:

    (10.5.1) — it has a trivial destructor,

    (10.5.2) — it is either a closure type (8.1.5.1), an aggregate type (11.6.1),
    or has at least one constexpr constructor or constructor template
    (possibly inherited (10.3.3) from a base class) that is not a copy or
    move constructor,

    (10.5.3) — if it is a union, at least one of its non-static data members is of non-volatile literal type, and

    (10.5.4) — if it is not a union, all of its non-static data members and base
    classes are of non-volatile literal types.




    [initializer_list.syn]/1





    1. An object of type initializer_list provides access to an array of objects of type const E. [ Note:A pair of pointers or a pointer plus a length would be obvious representations for initializer_list. initializer_list is used to implement initializer lists as specified in 11.6.4. Copying an initializer list does not copy the underlying elements. —end note ]




    That is the reason why it is not legal to declare a constexpr initializer_list object.






    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%2f54230229%2fwhy-is-an-initializer-list-of-enum-values-not-considered-a-constant-expression%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      5














      When you initialize a std::initializer_list it happens like this:




      [dcl.init.list] (emphasis mine)



      5 An object of type std​::​initializer_­list is constructed
      from an initializer list as if the implementation generated and
      materialized a prvalue of type “array of N const E”
      , where N is the
      number of elements in the initializer list. Each element of that array
      is copy-initialized with the corresponding element of the initializer
      list, and the std​::​initializer_­list object is constructed to
      refer to that array. [ Note: A constructor or conversion function
      selected for the copy shall be accessible in the context of the
      initializer list.  — end note ] If a narrowing conversion is required
      to initialize any of the elements, the program is ill-formed.
      [ Example:



      struct X {
      X(std::initializer_list<double> v);
      };
      X x{ 1,2,3 };


      The initialization will be implemented in a way roughly equivalent to
      this:



      const double __a[3] = {double{1}, double{2}, double{3}};
      X x(std::initializer_list<double>(__a, __a+3));


      assuming that the implementation can construct an initializer_­list
      object with a pair of pointers.  — end example ]




      How that temporary array gets used to initialize the std::initializer_list is what determines if the initializer_list is initialized with a constant expression. Ultimately, according to example (despite being non-normative), that initialization is going to take the address of the array, or its first element, which will produce a value of a pointer type. And that is not a valid constant expression.




      [expr.const] (emphasis mine)



      5 A constant expression is either a glvalue core constant
      expression that refers to an entity that is a permitted result of a
      constant expression (as defined below), or a prvalue core constant
      expression whose value satisfies the following constraints:




      • if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a
        constant expression,

      • if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such
        an object ([expr.add]), the address of a function, or a null pointer
        value, and

      • if the value is an object of class or array type, each subobject satisfies these constraints for the value.


      An entity is a permitted result of a constant expression if it is an
      object with static storage duration that is either not a temporary
      object or is a temporary object whose value satisfies the above
      constraints, or it is a function.




      If the array was however a static object, then that initializer would constitute a valid constant expression that can be used to initialize a constexpr object. Since std::initializer_list has an effect of lifetime extension on that temporary by [dcl.init.list]/6, when you declare group1 as a static object, clang and gcc seem allocate the array as a static object too, which makes the initialization's well-formedness subject only to whether or not std::initializer_list is a literal type and the constructor being used is constexpr.



      Ultimately, it's all a bit murky.






      share|improve this answer






























        5














        When you initialize a std::initializer_list it happens like this:




        [dcl.init.list] (emphasis mine)



        5 An object of type std​::​initializer_­list is constructed
        from an initializer list as if the implementation generated and
        materialized a prvalue of type “array of N const E”
        , where N is the
        number of elements in the initializer list. Each element of that array
        is copy-initialized with the corresponding element of the initializer
        list, and the std​::​initializer_­list object is constructed to
        refer to that array. [ Note: A constructor or conversion function
        selected for the copy shall be accessible in the context of the
        initializer list.  — end note ] If a narrowing conversion is required
        to initialize any of the elements, the program is ill-formed.
        [ Example:



        struct X {
        X(std::initializer_list<double> v);
        };
        X x{ 1,2,3 };


        The initialization will be implemented in a way roughly equivalent to
        this:



        const double __a[3] = {double{1}, double{2}, double{3}};
        X x(std::initializer_list<double>(__a, __a+3));


        assuming that the implementation can construct an initializer_­list
        object with a pair of pointers.  — end example ]




        How that temporary array gets used to initialize the std::initializer_list is what determines if the initializer_list is initialized with a constant expression. Ultimately, according to example (despite being non-normative), that initialization is going to take the address of the array, or its first element, which will produce a value of a pointer type. And that is not a valid constant expression.




        [expr.const] (emphasis mine)



        5 A constant expression is either a glvalue core constant
        expression that refers to an entity that is a permitted result of a
        constant expression (as defined below), or a prvalue core constant
        expression whose value satisfies the following constraints:




        • if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a
          constant expression,

        • if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such
          an object ([expr.add]), the address of a function, or a null pointer
          value, and

        • if the value is an object of class or array type, each subobject satisfies these constraints for the value.


        An entity is a permitted result of a constant expression if it is an
        object with static storage duration that is either not a temporary
        object or is a temporary object whose value satisfies the above
        constraints, or it is a function.




        If the array was however a static object, then that initializer would constitute a valid constant expression that can be used to initialize a constexpr object. Since std::initializer_list has an effect of lifetime extension on that temporary by [dcl.init.list]/6, when you declare group1 as a static object, clang and gcc seem allocate the array as a static object too, which makes the initialization's well-formedness subject only to whether or not std::initializer_list is a literal type and the constructor being used is constexpr.



        Ultimately, it's all a bit murky.






        share|improve this answer




























          5












          5








          5







          When you initialize a std::initializer_list it happens like this:




          [dcl.init.list] (emphasis mine)



          5 An object of type std​::​initializer_­list is constructed
          from an initializer list as if the implementation generated and
          materialized a prvalue of type “array of N const E”
          , where N is the
          number of elements in the initializer list. Each element of that array
          is copy-initialized with the corresponding element of the initializer
          list, and the std​::​initializer_­list object is constructed to
          refer to that array. [ Note: A constructor or conversion function
          selected for the copy shall be accessible in the context of the
          initializer list.  — end note ] If a narrowing conversion is required
          to initialize any of the elements, the program is ill-formed.
          [ Example:



          struct X {
          X(std::initializer_list<double> v);
          };
          X x{ 1,2,3 };


          The initialization will be implemented in a way roughly equivalent to
          this:



          const double __a[3] = {double{1}, double{2}, double{3}};
          X x(std::initializer_list<double>(__a, __a+3));


          assuming that the implementation can construct an initializer_­list
          object with a pair of pointers.  — end example ]




          How that temporary array gets used to initialize the std::initializer_list is what determines if the initializer_list is initialized with a constant expression. Ultimately, according to example (despite being non-normative), that initialization is going to take the address of the array, or its first element, which will produce a value of a pointer type. And that is not a valid constant expression.




          [expr.const] (emphasis mine)



          5 A constant expression is either a glvalue core constant
          expression that refers to an entity that is a permitted result of a
          constant expression (as defined below), or a prvalue core constant
          expression whose value satisfies the following constraints:




          • if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a
            constant expression,

          • if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such
            an object ([expr.add]), the address of a function, or a null pointer
            value, and

          • if the value is an object of class or array type, each subobject satisfies these constraints for the value.


          An entity is a permitted result of a constant expression if it is an
          object with static storage duration that is either not a temporary
          object or is a temporary object whose value satisfies the above
          constraints, or it is a function.




          If the array was however a static object, then that initializer would constitute a valid constant expression that can be used to initialize a constexpr object. Since std::initializer_list has an effect of lifetime extension on that temporary by [dcl.init.list]/6, when you declare group1 as a static object, clang and gcc seem allocate the array as a static object too, which makes the initialization's well-formedness subject only to whether or not std::initializer_list is a literal type and the constructor being used is constexpr.



          Ultimately, it's all a bit murky.






          share|improve this answer















          When you initialize a std::initializer_list it happens like this:




          [dcl.init.list] (emphasis mine)



          5 An object of type std​::​initializer_­list is constructed
          from an initializer list as if the implementation generated and
          materialized a prvalue of type “array of N const E”
          , where N is the
          number of elements in the initializer list. Each element of that array
          is copy-initialized with the corresponding element of the initializer
          list, and the std​::​initializer_­list object is constructed to
          refer to that array. [ Note: A constructor or conversion function
          selected for the copy shall be accessible in the context of the
          initializer list.  — end note ] If a narrowing conversion is required
          to initialize any of the elements, the program is ill-formed.
          [ Example:



          struct X {
          X(std::initializer_list<double> v);
          };
          X x{ 1,2,3 };


          The initialization will be implemented in a way roughly equivalent to
          this:



          const double __a[3] = {double{1}, double{2}, double{3}};
          X x(std::initializer_list<double>(__a, __a+3));


          assuming that the implementation can construct an initializer_­list
          object with a pair of pointers.  — end example ]




          How that temporary array gets used to initialize the std::initializer_list is what determines if the initializer_list is initialized with a constant expression. Ultimately, according to example (despite being non-normative), that initialization is going to take the address of the array, or its first element, which will produce a value of a pointer type. And that is not a valid constant expression.




          [expr.const] (emphasis mine)



          5 A constant expression is either a glvalue core constant
          expression that refers to an entity that is a permitted result of a
          constant expression (as defined below), or a prvalue core constant
          expression whose value satisfies the following constraints:




          • if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a
            constant expression,

          • if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such
            an object ([expr.add]), the address of a function, or a null pointer
            value, and

          • if the value is an object of class or array type, each subobject satisfies these constraints for the value.


          An entity is a permitted result of a constant expression if it is an
          object with static storage duration that is either not a temporary
          object or is a temporary object whose value satisfies the above
          constraints, or it is a function.




          If the array was however a static object, then that initializer would constitute a valid constant expression that can be used to initialize a constexpr object. Since std::initializer_list has an effect of lifetime extension on that temporary by [dcl.init.list]/6, when you declare group1 as a static object, clang and gcc seem allocate the array as a static object too, which makes the initialization's well-formedness subject only to whether or not std::initializer_list is a literal type and the constructor being used is constexpr.



          Ultimately, it's all a bit murky.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 3 hours ago

























          answered 4 hours ago









          StoryTellerStoryTeller

          95.4k12194259




          95.4k12194259

























              3














              It appears std::initializer_list does not yet (in C++17) fulfill the requirements of literal type (which is a requirement the type of a constexpr variable has to satisfy).



              A discussion on whether it does so in C++14 is found in this post: Why isn't std::initializer_list defined as a literal type?
              which itself was a followup to the post discussing Is it legal to declare a constexpr initializer_list object?



              I compared the citations provided in the C++14 related post (of the C++14 standard) to the final working draft (of the C++17 standard) and they are the same.
              So there is no explicit requirement that std::initializer_list should be a literal type.



              Citations from the final working draft of C++17 (n4659):



              [basic.types]/10.5




              (10.5) a possibly cv-qualified class type (Clause 12) that has all of the
              following properties:

              (10.5.1) — it has a trivial destructor,

              (10.5.2) — it is either a closure type (8.1.5.1), an aggregate type (11.6.1),
              or has at least one constexpr constructor or constructor template
              (possibly inherited (10.3.3) from a base class) that is not a copy or
              move constructor,

              (10.5.3) — if it is a union, at least one of its non-static data members is of non-volatile literal type, and

              (10.5.4) — if it is not a union, all of its non-static data members and base
              classes are of non-volatile literal types.




              [initializer_list.syn]/1





              1. An object of type initializer_list provides access to an array of objects of type const E. [ Note:A pair of pointers or a pointer plus a length would be obvious representations for initializer_list. initializer_list is used to implement initializer lists as specified in 11.6.4. Copying an initializer list does not copy the underlying elements. —end note ]




              That is the reason why it is not legal to declare a constexpr initializer_list object.






              share|improve this answer






























                3














                It appears std::initializer_list does not yet (in C++17) fulfill the requirements of literal type (which is a requirement the type of a constexpr variable has to satisfy).



                A discussion on whether it does so in C++14 is found in this post: Why isn't std::initializer_list defined as a literal type?
                which itself was a followup to the post discussing Is it legal to declare a constexpr initializer_list object?



                I compared the citations provided in the C++14 related post (of the C++14 standard) to the final working draft (of the C++17 standard) and they are the same.
                So there is no explicit requirement that std::initializer_list should be a literal type.



                Citations from the final working draft of C++17 (n4659):



                [basic.types]/10.5




                (10.5) a possibly cv-qualified class type (Clause 12) that has all of the
                following properties:

                (10.5.1) — it has a trivial destructor,

                (10.5.2) — it is either a closure type (8.1.5.1), an aggregate type (11.6.1),
                or has at least one constexpr constructor or constructor template
                (possibly inherited (10.3.3) from a base class) that is not a copy or
                move constructor,

                (10.5.3) — if it is a union, at least one of its non-static data members is of non-volatile literal type, and

                (10.5.4) — if it is not a union, all of its non-static data members and base
                classes are of non-volatile literal types.




                [initializer_list.syn]/1





                1. An object of type initializer_list provides access to an array of objects of type const E. [ Note:A pair of pointers or a pointer plus a length would be obvious representations for initializer_list. initializer_list is used to implement initializer lists as specified in 11.6.4. Copying an initializer list does not copy the underlying elements. —end note ]




                That is the reason why it is not legal to declare a constexpr initializer_list object.






                share|improve this answer




























                  3












                  3








                  3







                  It appears std::initializer_list does not yet (in C++17) fulfill the requirements of literal type (which is a requirement the type of a constexpr variable has to satisfy).



                  A discussion on whether it does so in C++14 is found in this post: Why isn't std::initializer_list defined as a literal type?
                  which itself was a followup to the post discussing Is it legal to declare a constexpr initializer_list object?



                  I compared the citations provided in the C++14 related post (of the C++14 standard) to the final working draft (of the C++17 standard) and they are the same.
                  So there is no explicit requirement that std::initializer_list should be a literal type.



                  Citations from the final working draft of C++17 (n4659):



                  [basic.types]/10.5




                  (10.5) a possibly cv-qualified class type (Clause 12) that has all of the
                  following properties:

                  (10.5.1) — it has a trivial destructor,

                  (10.5.2) — it is either a closure type (8.1.5.1), an aggregate type (11.6.1),
                  or has at least one constexpr constructor or constructor template
                  (possibly inherited (10.3.3) from a base class) that is not a copy or
                  move constructor,

                  (10.5.3) — if it is a union, at least one of its non-static data members is of non-volatile literal type, and

                  (10.5.4) — if it is not a union, all of its non-static data members and base
                  classes are of non-volatile literal types.




                  [initializer_list.syn]/1





                  1. An object of type initializer_list provides access to an array of objects of type const E. [ Note:A pair of pointers or a pointer plus a length would be obvious representations for initializer_list. initializer_list is used to implement initializer lists as specified in 11.6.4. Copying an initializer list does not copy the underlying elements. —end note ]




                  That is the reason why it is not legal to declare a constexpr initializer_list object.






                  share|improve this answer















                  It appears std::initializer_list does not yet (in C++17) fulfill the requirements of literal type (which is a requirement the type of a constexpr variable has to satisfy).



                  A discussion on whether it does so in C++14 is found in this post: Why isn't std::initializer_list defined as a literal type?
                  which itself was a followup to the post discussing Is it legal to declare a constexpr initializer_list object?



                  I compared the citations provided in the C++14 related post (of the C++14 standard) to the final working draft (of the C++17 standard) and they are the same.
                  So there is no explicit requirement that std::initializer_list should be a literal type.



                  Citations from the final working draft of C++17 (n4659):



                  [basic.types]/10.5




                  (10.5) a possibly cv-qualified class type (Clause 12) that has all of the
                  following properties:

                  (10.5.1) — it has a trivial destructor,

                  (10.5.2) — it is either a closure type (8.1.5.1), an aggregate type (11.6.1),
                  or has at least one constexpr constructor or constructor template
                  (possibly inherited (10.3.3) from a base class) that is not a copy or
                  move constructor,

                  (10.5.3) — if it is a union, at least one of its non-static data members is of non-volatile literal type, and

                  (10.5.4) — if it is not a union, all of its non-static data members and base
                  classes are of non-volatile literal types.




                  [initializer_list.syn]/1





                  1. An object of type initializer_list provides access to an array of objects of type const E. [ Note:A pair of pointers or a pointer plus a length would be obvious representations for initializer_list. initializer_list is used to implement initializer lists as specified in 11.6.4. Copying an initializer list does not copy the underlying elements. —end note ]




                  That is the reason why it is not legal to declare a constexpr initializer_list object.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited 3 hours ago

























                  answered 4 hours ago









                  P.WP.W

                  12.3k3843




                  12.3k3843






























                      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%2f54230229%2fwhy-is-an-initializer-list-of-enum-values-not-considered-a-constant-expression%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

                      Polycentropodidae

                      Magento 2 Error message: Invalid state change requested

                      Paulmy