我试图理解我收到的一些递归 C++ 模板代码,但遇到了一些奇怪的行为。出于某种原因,编译器似乎能够在编译时添加两个值,但必须在运行时进行左移。即便如此,只有当我尝试在启用 c++11 的情况下进行构建时才会出现问题。
代码(我已经归结,你稍后会看到)定义了两对模板——一对名为 shft
和 shft_aux
和一对名为 add
和 add_aux
递归生成它们自己。顺便说一句,add
模板不应该有用,它的唯一目的是演示问题,而不是生成实际的 min
值。
如果我在没有命令行参数的情况下编译这段代码,它编译得很好。但是,如果我指定 -std=c++11 -stdlib=libc++
,add_aux 上的 static_assert 仍然可以,但 shft_aux 上的 static_assert 现在会生成编译时错误,提示 static_assert expression is not整型常量表达式
。
为什么左移与加法的处理方式不同?
谢谢, 克里斯
附注我正在使用 clang++ 版本 Apple LLVM version 5.1 (clang-503.0.38)(基于 LLVM 3.4svn)
#include <climits>
template <unsigned size> struct shft; // forward
template <unsigned size>
struct shft_aux
{
static const int min = shft<size>::min;
};
template <unsigned size>
struct shft
{
typedef shft_aux<size - 1> prev;
static const int min = prev::min << CHAR_BIT;
};
// Base specialization of shft, puts an end to the recursion.
template <>
struct shft<1>
{
static const int min = SCHAR_MIN;
};
// -----
template <unsigned size> struct add; // forward
template <unsigned size>
struct add_aux
{
static const int min = add<size>::min;
};
template <unsigned size>
struct add
{
typedef add_aux<size - 1> prev;
static const int min = prev::min + CHAR_BIT;
};
// Base specialization of add, puts an end to the recursion.
template <>
struct add<1>
{
static const int min = SCHAR_MIN;
};
// -----
int main()
{
static_assert(shft_aux<sizeof(int)>::min < 0, "min is not negative");
static_assert(add_aux<sizeof(int)>::min < 0, "min is not negative");
return 0;
}
最佳答案
C++11 标准,[expr.shift]/2
The value of
E1 << E2
isE1
left-shiftedE2
bit positions; vacated bits are zero-filled. IfE1
has an unsigned type, [...]. Otherwise, ifE1
has a signed type and non-negative value, and E1*2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
[强调我的]
这受到 DR1457 的轻微影响这使得移动进入“符号位”定义的行为:
Otherwise, if
E1
has a signed type and non-negative value, and E1*2E2 is representable in the corresponding unsigned type of the result type [...].
无论如何,在 OP 中,E1
是负的,所以这仍然是未定义的行为。因此,它不允许在常量表达式中使用:
[expr.const]/2 A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression [...]
- [...]
- a result that is not mathematically defined or not in the range of representable values for its type;
那个点已经改变了(DR1313);在 n3485 中它说:
- an operation that would have undefined behavior [ Note: including, for example, signed integer over- flow (Clause 5), certain pointer arithmetic (5.7), division by zero (5.6), or certain shift operations (5.8) — end note ];
[类.静态数据]/3
If a non-volatile
const static
data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression
结论:转移SCHAR_MIN
不是常量表达式,因此您不能在静态数据成员的类内初始化程序中执行此操作。
提示:总是用-Wall -Wextra -pedantic
编译.不使用参数 IMO 对于 g++ 和兼容的编译器来说不是一个好主意。 g++/clang++ 默认使用 gnu99
模式 ( see clang doc ),它是 C++98 AFAIK 的扩展。此外,您会错过许多重要的警告。
关于c++ - 启用 c++11 时 c++ 递归模板的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23250651/