我正在尝试理解我已经提交的一些递归C ++模板代码,而且我遇到了一些奇怪的行为。出于某种原因,编译器似乎能够在编译时添加两个值,但是必须保留左移以用于运行时。即使这样,只有在我尝试使用c ++ 11构建时才会出现问题。
代码(我已经简化了,你稍后会看到)定义了两对模板 - 一对名为shft
和shft_aux
,一对名为add
和{{ 1}}以递归方式生成自己。顺便说一句,add_aux
模板不应该有用,它的唯一目的是演示问题,而不是生成实际的add
值。
如果我在没有命令行参数的情况下编译此代码,它编译就好了。但是如果我指定min
,则add_aux上的static_assert仍然可以,但是shft_aux上的static_assert现在会生成一个编译时错误-std=c++11 -stdlib=libc++
。
为什么左移处理不同于添加?
谢谢, 克里斯
P.S。我正在使用clang ++版本static_assert expression is not an integral constant expression
Apple LLVM version 5.1 (clang-503.0.38) (based on LLVM 3.4svn)
答案 0 :(得分:7)
C ++ 11 Standard,[expr.shift] / 2
E1 << E2
的值为E1
左移E2
位位置;空位是零填充的。如果E1
具有无符号类型,[...]。否则,如果E1
具有有符号类型和非负值,并且 E1 * 2 E2 可表示 在结果类型中,那就是结果值; 否则,行为未定义。
[强调我的]
DR1457稍微影响了这一点,这使转换为“符号位”定义的行为:
否则,如果
E1
具有有符号类型和非负值,并且 E1 * 2 E2 可在结果的相应无符号类型中表示输入[...]。
无论如何,在OP中,E1
是否定的,所以仍然是未定义的行为。因此,常量表达式:
[expr.const] / 2 条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式[...]
- [...]
- 未在数学上定义的结果或不在其类型的可表示值范围内的结果;
这一点已经改变(DR1313);在n3485它说:
- 一个具有未定义行为的操作[注意:包括,例如,有符号整数over- 流程(第5条),某些指针算术(5.7),除零(5.6)或某些移位操作(5.8) - 结束记录];
[class.static.data] / 3
如果非易失性
const static
数据成员是整数或枚举类型,则其在类定义中的声明可以指定大括号或等于初始值,其中作为赋值表达式的每个 initializer-clause 是一个常量表达式
结论:移位SCHAR_MIN
不是常量表达式,因此您不能在静态数据成员的类内初始值设定项中执行此操作。
提示:始终使用-Wall -Wextra -pedantic
进行编译。不使用参数IMO对于g ++和兼容的编译器来说是一个坏主意。 g ++ / clang ++默认使用gnu99
模式(see clang doc),它是C ++ 98 AFAIK的扩展。此外,你会错过很多重要的警告。