模板类中枚举中的整数溢出

Integer overflow in enum when in template class

本文关键字:整数 溢出 枚举      更新时间:2023-10-16

深入研究模板元编程,我发现c++中枚举的范围有一个奇怪的行为。我得到一个警告:表达式中的整数溢出,当看起来我实际上不想要超出enum范围的值时。下面是代码:

#include <iostream>
#include <limits>
template <int i>
class pow {
public:
        enum { result = 2*pow<i-1>::result};
};
template<>
class pow<0> {
public:
        enum { result = 1};
};
enum test { one, max = 4294967295 };
enum test_2 { last = 4294967295*2 };
int main() {
        std::cout << "pow<2>: t" << pow<2>::result << std::endl;
        std::cout << "pow<4>: t" << pow<4>::result << std::endl;
        std::cout << "pow<30>: t" << pow<30>::result << std::endl;
        std::cout << "pow<31>: t" << pow<31>::result << std::endl;
        std::cout << "max test: t" << 
                std::numeric_limits<std::underlying_type<test>::type>::max() << std::endl;
        std::cout << "max test_2: t" << 
                std::numeric_limits<std::underlying_type<test_2>::type>::max() << std::endl;
        return 0;
}
编译输出:

test.cpp:7:19: warning: integer overflow in expression [-Woverflow]
  enum { result = 2*pow<i-1>::result};
                   ^
test.cpp:7:7: warning: overflow in constant expression [-fpermissive]
  enum { result = 2*pow<i-1>::result};

程序输出:

pow<2>:         4
pow<4>:         16
pow<30>:        1073741824
pow<31>:        -2147483648
max test:       4294967295
max test_2:     18446744073709551615

为什么类pow中的enum有一个较小的范围?据我所知,对于每个' I '值,我都实例化了一个不同的类,因此它在其中有一个单独的枚举类型。因此,对于'i'> 31, enum应该是64位,就像test_2一样。我哪里错了?
我尝试了gcc 4.8和gcc 5.4,结果是一样的。

From cppreference.com

Unscoped枚举
enum name { enumerator = constexpr , enumerator = constexpr , ... } (1)
(…)
1)声明一个基础类型不固定的无作用域枚举类型(在本例中,基础类型为 int,或者,如果不是所有枚举数值都可以表示为int,则声明一个实现定义的更大的整数类型,可以表示所有枚举数值。如果枚举数列表为空,则底层类型就像该枚举只有一个值为0的枚举数一样。

因此,在模板中,编译器似乎选择int,最大值为+-2147483648(31位+ 1为符号)。在test中,编译器似乎认识到这是不够的,并选择unsigned int的最大值为4294967296(32位)。在test _2中,编译器认为这也不够,并选择64位的无符号类型(最大18446744073709551616)。

将枚举声明为

enum { result = 2*pow<i-1>::result, randomMax = *insert anything large here*};

就可以了。第二个值只需要强制编译器选择一个足够长的类型来保存您需要的所有值。