考虑以下代码片段:
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
声明。初始齐尔违反[Expr.Const]/2,指定:
表达式e是 core constant表达式,除非 E评估E,遵循抽象机的规则,将 评估以下表达式之一:
- 通过该国际标准的[CPP]条款[intro]中指定的不确定行为的操作[注: 例如,包括签名的整数溢出(条款[Expr]), 某些指针算术([Expr.Add]),零,或 某些班次操作 - 结束注];
因此,您不能使用1<<x
初始化y
。该分支永远不会被执行并可以消除并不重要。海湾合作委员会仍然有义务验证其语义正确。
正如讲故事的人所解释的那样,这是预期的行为,因为负数的左数是未定义的行为,而导致Ub的表达不能用于核心常数表达式(事实,您在运行时不尝试访问该表达式的结果并没有改变您要求编译器在编译期间对其进行评估的事实)。
如果您的分支实际上取决于模板参数,则可以使用if constexpr
:
template<int x>
constexpr int foo() {
if constexpr (x >= 0) {
constexpr int y = 1 << x;
return y;
}
return 0;
}
编辑:正如讲故事的问题的答案所解释的那样,这仅在模板中起作用,并且只有条件取决于模板参数(答案中的更详细说明)。