2016-07-03 9 views
4

テンプレートのメタプログラミングを掘り起こすと、C++の列挙型の範囲で奇妙な動作が検出されました。 警告:式で整数オーバーフローが発生する。実際にはenumの範囲外の値は望ましくないようだ。テンプレートクラスのときに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で列挙型が小さい範囲を持っているのはなぜ?限り、私が理解する限り、私は別のクラスがインスタンス化されているので、それは別の列挙型を持っています。結果として、 'i'> 31の場合、enumはtest_2のように64ビットでなければなりません。どこが間違っていますか?
私はgcc 4.8とgcc 5.4を試しましたが、結果は同じです。

+0

C++ 11を使用している場合は、 'enum'の代わりに' static constexpr'変数を使用するか、 'enum:long int {...};' – Nelfeal

答えて

0

(1)
(...)
1)は、その基礎となるタイプ、この場合には(固定されていない対象範囲外の列挙型を宣言し
enum name { enumerator = constexpr , enumerator = constexpr , ... }スコープ外列挙

cppreference.comから基底型はintまたはすべての列挙子の値がintとして表現できる場合は、すべての列挙子値を表すことができる実装定義のより大きい整数型。列挙子リストが空の場合、th基になる型は列挙型に値0の単一の列挙子があるかのようです)。

したがって、コンパイラはintを選択したように見えます。最大値は+ -2147483648(符号は31ビット+ 1)です。 testでは、コンパイラはこれで十分ではないと認識していたようで、最大値4294967296(32ビット)のunsigned intを選択します。 test _2では、コンパイラはこれも十分ではないと認識し、64ビット(最大18446744073709551616)の符号なしタイプを選択します。

トリックを行う必要があります

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

として、あなたの列挙型を宣言。 2番目の値は、必要なすべての値を保持するのに十分な長さの型をコンパイラが選択するように強制するだけです。

+0

のような列挙型を指定することができます答えを完成させるために: –

+1

最初に、 'テンプレート'は大きな数字のためのより良いオプションです。 2番目: 'result = pow :: result * 2'の型について考える。 _pow <0> :: result_はintであり、2を掛けてintを与えるので、pow <1> :: resultはintであり、そのパターンが繰り返されます。そのような方法で再帰の型を変更する方法はありません(enumは具体的な型を持っているので、私は気づいていません)。乗算がlong型を与えるため、 'enum {result = 2l * pow :: result}'で十分です。コンパイラは_result_にどの型を使うかを決定します。 –

関連する問題