启用 c++11 时 c++ 递归模板的奇怪行为
Strange behavior with c++ recursive templates when c++11 is enabled
我正在尝试理解我收到的一些递归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 an integral constant expression
。
为什么左移与加法的处理方式不同?
谢谢克里斯
附言我正在使用 clang++ 版本 Apple LLVM version 5.1 (clang-503.0.38) (based on 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 Standard, [expr.shift]/2
E1 << E2
的值E1
位位置左移E2
;空出的位填充为零。如果E1
具有无符号类型,则 [...]。否则,如果E1
具有有符号类型和非负值,并且 E1*2E2 是可表示的 在结果类型中,这就是结果值;否则,行为是未定义的。
[强调我的]
这受到DR1457的轻微影响,这使得转换为"符号位">定义的行为:
否则,如果
E1
具有有符号类型和非负值,并且 E1*2E2 可在结果类型的相应无符号类型中表示 [...]。
无论如何,在OP中,E1
是负面的,所以这仍然是未定义的行为。因此,不允许在常量表达式中使用它:
[expr.const]/2 条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式 [...]
- [...]
- 未在数学上定义或不在其类型的可表示值范围内的结果;
这一点已经改变(DR1313(;在n3485中它说:
- 具有未定义行为的操作 [ 注意:例如,包括有符号整数 over- 流(条款 5(、某些指针算术 (5.7(、除以零 (5.6( 或某些移位运算 (5.8( — 尾注 ];
[class.static.data]/3
如果非易失性
const static
数据成员是整型或枚举类型,则其在类定义中的声明可以指定大括号或等于初始值设定项,其中作为赋值表达式的每个初始值设定项子句都是常量表达式
结论:移位SCHAR_MIN
不是一个常量表达式,因此不能在静态数据成员的类内初始值设定项中执行此操作。
提示:始终使用 -Wall -Wextra -pedantic
编译。对于 g++ 和兼容的编译器来说,不使用参数 IMO 是一个坏主意。g++/clang++默认使用gnu99
模式(参见clang doc(,这是C++98 AFAIK的扩展。此外,您会错过许多重要的警告。
- MSVC是否支持C++11样式的属性而不是__declspec
- 创建LinkedList退出,返回代码为-11(SIGSEGV)
- 我可以将一个用clang c++11编译的对象与另一个用c++17编译的对象链接起来吗
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 如何将模板转换为C++11之前的模板
- c++11评估顺序(未定义的行为)
- C++中的VLA,扩展名为std=C++11
- 代码在我的计算机上运行良好,但是在将其提交给coursera时遇到未知的信号11问题
- "类模板示例<int>;"语句对 C++11 是什么意思?
- this_thread::sleep_for和计时时钟之间的关系是否由C++11标准指定
- 如何使用lock_guard在c++11中实现scoped_lock功能
- C++11 中不同类型的对象的 std::array 的替代方案
- 为什么 -mmacosx-version-min=10.10 不阻止使用标记为从 10.11 开始的函数?
- 为什么我的C++代码中出现'Segmentation Fault: 11'行?
- 强枚举类型定义:Clang Bug 还是 C++11 标准不确定性?
- 别名模板的专业化 C++11 中没有开销的最佳替代方案
- STLPort using C++11
- Qt 5.11.2 (Clang 8.0 (Apple), 64 位), 找不到 QJSEngine 文件
- 在 C++11 中,如何查找并返回以给定字符串开头的字符串向量中的所有项?
- C++11 迭代向量的新方法?