Why is an initializer_list of enum values not considered a constant expression?
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
add a comment |
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
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
add a comment |
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
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
c++ language-lawyer c++17 constexpr
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
add a comment |
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
add a comment |
2 Answers
2
active
oldest
votes
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.
add a comment |
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
- 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.
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
add a comment |
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.
add a comment |
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.
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.
edited 3 hours ago
answered 4 hours ago
StoryTellerStoryTeller
95.4k12194259
95.4k12194259
add a comment |
add a comment |
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
- 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.
add a comment |
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
- 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.
add a comment |
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
- 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.
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
- 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.
edited 3 hours ago
answered 4 hours ago
P.WP.W
12.3k3843
12.3k3843
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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