考虑以下代码片段:
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]
我可以猜到这可能来自哪里:constexpr
上的y
声明使GCC在编译时评估y
,它可能是负数。删除constexpr
可修复错误。
但是,标准是否存在未定义的行为?条件始终为false,因此永远不会使用y
的值。
在我的实际代码中,x
是一个模板参数,可能是也可能不是。
答案 0 :(得分:4)
GCC抱怨是因为您对y
的定义明确是一个格式错误的constexpr
声明。 initialzier违反了[expr.const]/2,其中指定了:
表达式e是核心常量表达式 ,除非 根据抽象机器的规则评估e 评估以下表达式之一:
- 具有本国际标准的[intro]至[cpp]条款中指定的未定义行为的操作[注意: 包括,例如,有符号整数溢出(Clause [expr]), 某些指针算术([expr.add]),除以零,或 某些班次操作 - 结束说明];
因此,您无法使用1<<x
初始化y
。分支永远不会被执行并且可以被消除并不重要。海湾合作委员会仍有义务核实其语义正确。
答案 1 :(得分:2)
就像StoryTeller解释的那样,这是预期的行为,因为左移由负数表示未定义的行为,导致UB的表达式不能用于核心常量表达式(事实上你不试图在运行时期间访问该表达式的结果并不会改变编译器在编译期间对其进行评估的事实。
如果您的分支实际上依赖于模板参数,您可以使用if constexpr
:
template<int x>
constexpr int foo() {
if constexpr (x >= 0) {
constexpr int y = 1 << x;
return y;
}
return 0;
}
编辑:正如StoryTeller's question的答案所解释的那样,这只能在模板内部工作,并且只有条件取决于模板参数(答案中有更详细的解释)。