web-dev-qa-db-ja.com

C ++列挙型は署名されていますか?

C++列挙型は署名されていますか?また、拡張により、入力が<=最大値であることを確認して入力を検証し、最小値> =を省略することは安全です(0で開始し、1ずつ増加すると仮定します)?

103
Matt

特定の表現に頼るべきではありません。以下をお読みください link 。また、標準では、一部の値がintまたはunsigned intに収まらない限り、intより大きくてはならないことを除いて、enumの基になる型として使用される整数型は実装定義であると述べています。

要するに、enumが署名されているか署名されていないかに依存することはできません。

58
zvrba

ソースに行きましょう。 C++ 03標準(ISO/IEC 14882:2003)ドキュメントの7.2-5(列挙宣言)の内容は次のとおりです。

列挙の基になる型は、列挙で定義されたすべての列挙子値を表すことができる整数型です。列挙子の値がintまたはunsigned intに収まらない場合を除き、基になる型がintより大きくなってはならないことを除き、列挙の基になる型として使用される整数型は実装定義です。

要するに、コンパイラーが選択できるようになります(明らかに、いくつかの列挙値に負の数がある場合、符号が付けられます)。

98
Michael Burr

あなたはそれらが署名されているか署名されていないことに依存すべきではありません。明示的に署名または署名なしにする場合は、次を使用できます。

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum
20
Adam Rosenfield

署名されているか署名されていないかに依存しないでください。標準によれば、どの整数型が列挙型の基になる型として使用されるかは実装定義です。ただし、ほとんどの実装では、符号付き整数です。

C++ 0xでは、 厳密に型指定された列挙 が追加され、次のような列挙型を指定できるようになります。

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum

ただし、今でも、enumを次のような変数またはパラメータータイプとして使用することで、いくつかの簡単な検証を実現できます。

enum Fruit { Apple, Banana };

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit
                    // even though it has the same value as banana.
15
Matt

いくつかの古い回答にも44の賛成票がありましたが、私はそれらすべてに反対する傾向があります。要するに、enumのunderlying typeを気にする必要はないと思います。

まず、C++ 03列挙型は、符号の概念を持たない独自の特殊な型です。 C++ 03標準dcl.enumから

7.2 Enumeration declarations 
5 Each enumeration defines a type that is different from all other types....

したがって、enum型の符号について、つまり<演算子を使用して2つのenumオペランドを比較する場合、実際には暗黙的にenum型を整数型に変換することについて話します。 重要なのはこの整数型の符号です。また、enumを整数型に変換する場合、次のステートメントが適用されます。

9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).

そして、明らかに、列挙型の基礎となるタイプは、インテグラルプロモーションとは何の関係もありません。標準では、以下のようにインテグラルプロモーションが定義されています。

4.5 Integral promotions conv.prom
.. An rvalue of an enumeration type (7.2) can be converted to an rvalue of the first of the following types that can represent all the values of the enumeration
(i.e. the values in the range bmin to bmax as described in 7.2: int, unsigned int, long, or unsigned long.

したがって、列挙型がsigned intになるかunsigned intになるかは、signed intが列挙型の基になる型ではなく、定義された列挙子のすべての値を含むことができるかどうかによって決まります。

関連する質問を参照してください C++ Enum型の符号が整数型に変換した後に正しくない

5
JavaMan

コンパイラは、enumが署名されているかどうかを判断できます。

列挙型を検証する別の方法は、列挙型自体を変数型として使用することです。例えば:

enum Fruit
{
    Apple = 0,
    Banana,
    Pineapple,
    Orange,
    Kumquat
};

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit even though it has the same value as banana.
5
Cristián Romo

他の人がすでに署名/署名なしについて述べていることに加えて、標準は列挙型の範囲について言っています:

7.2(6):「e(min)が最小の列挙子であり、e(max)が最大の列挙の場合、列挙の値b(min) to b(max)の範囲の基になる型の値です。ここでb(min)およびb(max)は、それぞれe(min)およびe(max)を格納できる最小ビットフィールドの最小値と最大値です。列挙を定義することは可能です。列挙子によって定義されていない値があります。」

たとえば、次のとおりです。

enum { A = 1, B = 4};

e(min)は1、e(max)は4)の列挙型を定義します。基になる型が符号付き整数の場合、必要な最小ビットフィールド実装のintが2の補数である場合、enumの有効範囲は-8〜7です。基になる型が符号なしの場合、3ビットであり、範囲は0〜7です。コンパイラのドキュメントを確認してください気にする場合(たとえば、列挙子以外の整数値を列挙型にキャストする場合、値が列挙の範囲内にあるかどうかを知る必要があります-結果の列挙値が指定されていない場合)。

これらの値が関数への有効な入力であるかどうかは、列挙型の有効な値であるかどうかとは異なる問題になる可能性があります。あなたのチェックコードはおそらく後者ではなく前者を心配しているので、この例では少なくとも> = Aと<= Bをチェックする必要があります。

4
Steve Jessop

将来的には、C++ 0xで 厳密に型指定された列挙 が利用可能になり、いくつかの利点(型安全性、明示的な基本型、明示的なスコープなど)が得られます。これにより、タイプのサインをより確実に確認できます。

4
Kris Kumler

std::is_signed<std::underlying_type +スコープ付き列挙型をデフォルトでintでチェックします

https://en.cppreference.com/w/cpp/language/enum 意味:

main.cpp

#include <cassert>
#include <iostream>
#include <type_traits>

enum Unscoped {};
enum class ScopedDefault {};
enum class ScopedExplicit : long {};

int main() {
    // Implementation defined, let's find out.
    std::cout << std::is_signed<std::underlying_type<Unscoped>>() << std::endl;

    // Guaranteed. Scoped defaults to int.
    assert((std::is_same<std::underlying_type<ScopedDefault>::type, int>()));

    // Guaranteed. We set it ourselves.
    assert((std::is_same<std::underlying_type<ScopedExplicit>::type, long>()));
}

GitHubアップストリーム

コンパイルして実行します:

g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main

出力:

0

Ubuntu 16.04、GCC 6.4.0でテスト済み。