【发布时间】:2019-06-11 07:36:16
【问题描述】:
在以下代码中(在本地和 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-ids 是非常量的,尽管它们显然实际上是众所周知的编译时常量值。
其次,MSVC 抱怨在生命周期之外读取,但 group1 的生命周期及其值应该在 print 的整个使用过程中延伸。
第三,gcc 有一个奇怪的 const-vs-non-const 抱怨,我无法理解,因为初始化列表总是 const。
最后,如果 constexpr 被删除,除了 gcc 之外的所有代码都将愉快地编译和运行这段代码而不会出现任何问题。当然,在这种情况下它不是必要的,但我看不出有什么好的理由让它不起作用。
同时 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 本身,而不是枚举值。但是语法更烦人。 (使用合适的make_array 实现会稍微不那么烦人,但我仍然不明白为什么原始版本无效。)
constexpr auto const group1 = std::array{ A, D };
也可以使用,这要归功于 C++17 模板归纳。虽然现在print 不能接受initializer_list;它必须在通用容器/迭代器概念上进行模板化,这很不方便。
【问题讨论】:
-
我喜欢这个问题,我想知道它是否是一个错误(可以登录到编译器的跟踪器上吗?)。只是出于好奇,它在 Enum 类中的表现如何?
-
枚举类的行为完全一样,除了在
print中,您需要显式转换为int,因为隐式转换被禁用。 -
即使没有枚举(只是简单的 int),我在 VS 2019 中得到“表达式没有计算为常量”,而 GCC 10 和 clang 10 编译如下:
#include <initializer_list> constexpr static std::initializer_list<int> a = {1, 2};
标签: c++ language-lawyer c++17 constexpr