constexpr-evaluating negative bitshift 时的未定义行为?
Undefined behavior when constexpr-evaluating negative bitshift?
考虑以下代码片段:
int main(){
constexpr int x = -1;
if(x >= 0){
constexpr int y = 1<<x;
}
}
GCC 7(可能还有其他版本的 GCC)拒绝编译它并说:
error: right operand of shift expression '(1 << -1)' is negative [-fpermissive]
我可以猜到这可能是从哪里来的:y
上的 constexpr
声明使 GCC 在编译时计算 y
,它可能是负数。删除 constexpr
修复错误。
但是,这是标准未定义的行为吗?条件始终为假,因此永远不会使用 y
的值。
在我的实际代码中,x
是一个模板参数,可以为负数,也可以不为负数。
GCC 抱怨是因为您对 y
的定义显然是格式错误的 constexpr
声明。 initialzier 违反了 [expr.const]/2,它指定:
An expression e is a core constant expression unless the
evaluation of e, following the rules of the abstract machine, would
evaluate one of the following expressions:
- an operation that would have undefined behavior as specified in Clauses [intro] through [cpp] of this International Standard [ Note:
including, for example, signed integer overflow (Clause [expr]),
certain pointer arithmetic ([expr.add]), division by zero, or
certain shift operations — end note ] ;
所以不能用1<<x
初始化y
。分支永远不会被执行并且可以被消除并不重要。 GCC 仍然有义务验证它在语义上的正确性。
正如 StoryTeller 所解释的那样,这是预期的行为,因为左移负数是未定义的行为,导致 UB 的表达式不能用于核心常量表达式(事实上你不尝试在运行时访问该表达式的结果不会改变您要求编译器在编译时对其求值的事实。
如果您的分支实际上依赖于模板参数,您可以使用 if constexpr
:
解决此问题
template<int x>
constexpr int foo() {
if constexpr (x >= 0) {
constexpr int y = 1 << x;
return y;
}
return 0;
}
编辑:正如 的答案所解释的那样,这仅在模板内有效,并且仅当条件取决于模板参数时(答案中有更详细的解释)。
考虑以下代码片段:
int main(){
constexpr int x = -1;
if(x >= 0){
constexpr int y = 1<<x;
}
}
GCC 7(可能还有其他版本的 GCC)拒绝编译它并说:
error: right operand of shift expression '(1 << -1)' is negative [-fpermissive]
我可以猜到这可能是从哪里来的:y
上的 constexpr
声明使 GCC 在编译时计算 y
,它可能是负数。删除 constexpr
修复错误。
但是,这是标准未定义的行为吗?条件始终为假,因此永远不会使用 y
的值。
在我的实际代码中,x
是一个模板参数,可以为负数,也可以不为负数。
GCC 抱怨是因为您对 y
的定义显然是格式错误的 constexpr
声明。 initialzier 违反了 [expr.const]/2,它指定:
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
- an operation that would have undefined behavior as specified in Clauses [intro] through [cpp] of this International Standard [ Note: including, for example, signed integer overflow (Clause [expr]), certain pointer arithmetic ([expr.add]), division by zero, or certain shift operations — end note ] ;
所以不能用1<<x
初始化y
。分支永远不会被执行并且可以被消除并不重要。 GCC 仍然有义务验证它在语义上的正确性。
正如 StoryTeller 所解释的那样,这是预期的行为,因为左移负数是未定义的行为,导致 UB 的表达式不能用于核心常量表达式(事实上你不尝试在运行时访问该表达式的结果不会改变您要求编译器在编译时对其求值的事实。
如果您的分支实际上依赖于模板参数,您可以使用 if constexpr
:
template<int x>
constexpr int foo() {
if constexpr (x >= 0) {
constexpr int y = 1 << x;
return y;
}
return 0;
}
编辑:正如