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