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.)
constexpr auto const group1 = std::array{ A, D };
Also works, thanks to C++17 template induction. Though now print
can't take an initializer_list
; it has to be templated on a generic container/iterator concept, which is inconvenient.