为什么 if constexpr 不能使这个核心常量表达式错误消失?
Why doesn't an if constexpr make this core constant expression error disappear?
参照。用于初始化 constexpr
变量 y
的核心常量表达式格式错误。给定的就这么多。
但是如果我尝试将 if
变成 if constexpr
:
template <typename T>
void foo() {
constexpr int x = -1;
if constexpr (x >= 0){
constexpr int y = 1 << x;
}
}
int main(){
foo<int>();
}
错误仍然存在。 GCC 7.2 仍然提供:
error: right operand of shift expression '(1 << -1)' is negative [-fpermissive]
但我认为语义检查应该在废弃的分支上不进行预制。
通过 constexpr
lambda 进行间接寻址确实有帮助,但是:
template <typename T>
void foo(){
constexpr int x = -1;
constexpr auto p = []() constexpr { return x; };
if constexpr (x >= 0){
constexpr int y = 1<<p();
}
}
y
上的 constexpr
说明符似乎改变了检查丢弃分支的方式。这是预期的行为吗?
@max66 很友好地检查了其他实现。他报告说 GCC (7.2.0 / Head 8.0.0) 和 Clang (5.0.0 / Head 6.0.0) 都可以重现该错误。
我不确定您为什么希望不检查分支。根据 [stmt.if]p2:
,if 分支唯一一次是 "not checked" 是当它是模板的一部分而不是 实例化 时
During the instantiation of an enclosing templated
entity (Clause 17), if the condition is not value-dependent after its instantiation, the discarded substatement
(if any) is not instantiated.
您的代码似乎不适用这种情况。
标准并没有对 if constexpr
的 丢弃语句 说太多。关于这些,[stmt.if] 中基本上有两个陈述:
- 在封闭模板中,丢弃的语句不会被实例化。
- 从废弃的语句中引用的名称不需要定义 ODR。
这些都不适用于您的使用:编译器抱怨 constexpr
if 初始化是正确的。请注意,当您想利用 实例化 失败时,您需要使条件依赖于模板参数:如果该值不依赖于模板参数,则失败当模板被 定义 时发生。例如,这段代码仍然失败:
template <typename T>
void f() {
constexpr int x = -1;
if constexpr (x >= 0){
constexpr int y = 1<<x;
}
}
但是,如果使 x
依赖于类型 T
就可以了,即使 f
是用 int
:
实例化的
template <typename T>
void f() {
constexpr T x = -1;
if constexpr (x >= 0){
constexpr int y = 1<<x;
}
}
int main() {
f<int>();
}
请注意,对于 Constexpr If 丢弃的语句:
the discarded statement can't be ill-formed for every possible specialization:
要解决此问题,您可以根据模板参数进行声明,例如
template<typename T, int X> struct dependent_value { constexpr static int V = X; };
template <typename T>
void foo() {
constexpr int x = -1;
if constexpr (x >= 0){
constexpr int y = 1 << dependent_value<T, x>::V;
}
}
参照constexpr
变量 y
的核心常量表达式格式错误。给定的就这么多。
但是如果我尝试将 if
变成 if constexpr
:
template <typename T>
void foo() {
constexpr int x = -1;
if constexpr (x >= 0){
constexpr int y = 1 << x;
}
}
int main(){
foo<int>();
}
错误仍然存在。 GCC 7.2 仍然提供:
error: right operand of shift expression '(1 << -1)' is negative [-fpermissive]
但我认为语义检查应该在废弃的分支上不进行预制。
通过 constexpr
lambda 进行间接寻址确实有帮助,但是:
template <typename T>
void foo(){
constexpr int x = -1;
constexpr auto p = []() constexpr { return x; };
if constexpr (x >= 0){
constexpr int y = 1<<p();
}
}
y
上的 constexpr
说明符似乎改变了检查丢弃分支的方式。这是预期的行为吗?
@max66 很友好地检查了其他实现。他报告说 GCC (7.2.0 / Head 8.0.0) 和 Clang (5.0.0 / Head 6.0.0) 都可以重现该错误。
我不确定您为什么希望不检查分支。根据 [stmt.if]p2:
,if 分支唯一一次是 "not checked" 是当它是模板的一部分而不是 实例化 时During the instantiation of an enclosing templated entity (Clause 17), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.
您的代码似乎不适用这种情况。
标准并没有对 if constexpr
的 丢弃语句 说太多。关于这些,[stmt.if] 中基本上有两个陈述:
- 在封闭模板中,丢弃的语句不会被实例化。
- 从废弃的语句中引用的名称不需要定义 ODR。
这些都不适用于您的使用:编译器抱怨 constexpr
if 初始化是正确的。请注意,当您想利用 实例化 失败时,您需要使条件依赖于模板参数:如果该值不依赖于模板参数,则失败当模板被 定义 时发生。例如,这段代码仍然失败:
template <typename T>
void f() {
constexpr int x = -1;
if constexpr (x >= 0){
constexpr int y = 1<<x;
}
}
但是,如果使 x
依赖于类型 T
就可以了,即使 f
是用 int
:
template <typename T>
void f() {
constexpr T x = -1;
if constexpr (x >= 0){
constexpr int y = 1<<x;
}
}
int main() {
f<int>();
}
请注意,对于 Constexpr If 丢弃的语句:
the discarded statement can't be ill-formed for every possible specialization:
要解决此问题,您可以根据模板参数进行声明,例如
template<typename T, int X> struct dependent_value { constexpr static int V = X; };
template <typename T>
void foo() {
constexpr int x = -1;
if constexpr (x >= 0){
constexpr int y = 1 << dependent_value<T, x>::V;
}
}