В следующем коде (проверено локально и на 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 не может быть скомпилирован с:
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'
(все относятся к строке, содержащей constexpr
)
Clang 8 (HEAD) сообщает:
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) сообщает:
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>
Почему?
Во-первых, все они, по-видимому, считают enum-идентификаторы непостоянными, несмотря на то, что они на самом деле являются общеизвестными постоянными значениями времени компиляции.
Во-вторых, MSVC жалуется на время вне чтения, но время жизни group1
и его значения должны увеличиваться на протяжении всего использования в print
.
В-третьих, у gcc есть странная жалоба const-vs-non-const, которую я не могу понять, поскольку списки инициализаторов всегда постоянны.
Наконец, все, кроме gcc, с радостью скомпилируют и запустят этот код без проблем, если удалить constexpr
. Конечно, в этом случае не обязательно , но я не вижу веских причин, чтобы он не работал.
Между тем, gcc будет компилировать и запускать код только в том случае, если тип параметра будет изменен на std::initializer_list<const Types>
- и это изменение приведет к сбою компиляции как в MSVC, так и в clang.
(Интересно: gcc 8 с изменением типа параметра успешно компилирует и запускает код, включающий constexpr
, где gcc 9 выдает ошибки.)
FWIW, изменив объявление на следующее:
constexpr auto const group1 = std::array<Types, 2>{ A, D };
Компилируется и запускается на всех трех компиляторах. Так что, скорее всего, сам initializer_list
ведет себя не так, как значения enum. Но синтаксис более раздражает. (Это немного менее раздражает при подходящей реализации make_array
, но я до сих пор не понимаю, почему оригинал недействителен.)
constexpr auto const group1 = std::array{ A, D };
Также работает, благодаря вводу шаблона C ++ 17. Хотя сейчас print
не может взять initializer_list
; он должен быть основан на общей концепции контейнера / итератора, что неудобно.