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


107

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


符号を必要とするコンテキストで列挙型を使用している場合、実際には列挙型を暗黙的に整数型に変換することについて話している。C ++ 03標準では、これはIntegral Promotionによって行われると述べており、列挙型の基礎となるタイプに関連するものは何もありません。それで、ここでのすべての回答が、基になる型が標準で定義されていないことに言及している理由がわかりませんか?:私はここbehaviou期待説明stackoverflow.com/questions/24802322/...
JavaMan

回答:


60

特定の表現に依存するべきではありません。次のリンクを読んでください。また、標準では、列挙型の基本型として使用される整数型は実装定義であると述べていますが、ある値がintまたはunsigned intに収まらない場合を除いて、intより大きくなることはありません。

つまり、列挙型が署名されているか、署名されていないかに依存することはできません。


28
Michael Burrの回答(標準を引用しています)は、型が「列挙型で定義されているすべての列挙子の値を表す」ことができるため、列挙型の値を負の値として定義する場合、署名されていると信頼できることを実際に意味します。
Samuel Harmer

101

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

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

要するに、コンパイラーが選択することになります(明らかに、列挙値の一部に負の数がある場合は、署名されます)。


すべての列挙値が小さい正の整数の場合、コンパイラの推測を回避して、基になる符号なしの型を使用するように指示するにはどうすればよいですか?(コンパイラーがintを選択していて、intがオーバーフローを被っているので、UBsanの発見をキャッチしています。値は符号なしで正であり、デクリメントまたは「負のストライド」を提供するために符号なしラップに依存しています)。
jww 2017

@jww-それはあなたが正確にあなたが使用しているコンパイラーに依存すると思います。標準は基礎となるタイプを指示せず、これを実装に任せるため、ツールのドキュメントを調べて、このオプションが可能かどうかを確認する必要があります。コードで特定の動作を保証したい場合は、式で使用する列挙型メンバーをキャストしてみませんか?
ysap 2018

22

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

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

11
将来のC ++ 0x標準のみ。
dalle

3
@dalle Microsoftコンパイラーは、型付き列挙型msdn.microsoft.com/en-us/library/2dzy4k6e(v=vs.80).aspxを
teodozjan

15

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

C ++ 0xでは、強く型付けされた列挙が追加され、次のような列挙型を指定できます。

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned 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.

あなたの2番目の例は少し混乱していると思います:)
Miral

5

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

enumを検証する別の方法は、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

いくつかの古い回答でさえ44票を得ましたが、私はそれらすべてに反対する傾向があります。つまり、underlying type列挙型について気にする必要はないと思います。

まず、C ++ 03 Enum型は、符号の概念がない独自の型です。C ++ 03標準以降dcl.enum

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

したがって、列挙型の符号について話しているとき、たとえば<演算子を使用して2つの列挙型オペランドを比較しているとき、実際には列挙型をある整数型に暗黙的に変換することについて話している。重要なのは、この一体型のしるしです。そして、列挙型を整数型に変換するとき、このステートメントが適用されます:

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

そして、どうやら、列挙型の基礎となるタイプはIntegral Promotionとは何の関係もありません。規格では次のようにIntegral Promotionが定義されているため、

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 intunsigned intかどうかに依存してsigned int定義され列挙子、ない列挙型の基になる型のすべての値を含めることができます。

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


でコンパイルするときに問題になります-Wsign-conversion。これは、コード内の意図しない間違いを見つけるのに役立ちます。ただし、標準を引用し、列挙型に関連付けられている型(対)がないことを指摘する場合は+1signedunsigned
jww 2015


4

署名済み/署名なしについて他の人がすでに言ったことに加えて、列挙型の範囲について標準が言っていることは次のとおりです:

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

だから例えば:

enum { A = 1, B = 4};

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

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


0

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でテスト済み。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.