c++ - 启用 c++11 时 c++ 递归模板的奇怪行为

标签 c++ templates c++11 recursion clang++

我试图理解我收到的一些递归 C++ 模板代码,但遇到了一些奇怪的行为。出于某种原因,编译器似乎能够在编译时添加两个值,但必须在运行时进行左移。即便如此,只有当我尝试在启用 c++11 的情况下进行构建时才会出现问题。

代码(我已经归结,你稍后会看到)定义了两对模板——一对名为 shftshft_aux 和一对名为 addadd_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 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned type, [...]. Otherwise, if E1 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/

相关文章:

c++ - 如何在 C++ 中将 "new"用于多边形

c++ - 设备函数指针作为模板参数

javascript - Handlebars.js:使用部分模板,就像使用普通的完整模板一样

c++ - Callback 类模板的实际用途是什么?

c++ - 我如何将 "map"参数包

c++11 - 将计时持续时间转换为 time_point

c++ - istream::read 和每个样本的 wav 格式位

c++ - 跨模块共享内存对象

c++ - 类成员在构造函数中和构造函数外的不同值

c++ - 将 boost::signals2::trackable 与 lambda 一起使用