Výčty (C++)
Výčet je uživatelem definovaný typ, který se skládá ze sady pojmenovaných integrálních konstant, které se označují jako enumerátory.
Poznámka
Tento článek se zabývá typem jazyka enum
C++ standardu ISO a s vymezeným typem (nebo typem silného typu), enum class
který je zaveden v jazyce C++11. Informace o typech nebo typech public enum class
v C++/CLI a C++/CX najdete v tématu enum class
(C++/CLI a C++/CX).private enum class
Syntaxe
enum-name
:
identifier
enum-specifier
:
enum-head
{
enumerator-list
Rozhodnout}
enum-head
{
enumerator-list
,
}
enum-head
:
enum-key
attribute-specifier-seq
optenum-head-name
optenum-base
enum-head-name
:
nested-name-specifier
Rozhodnoutidentifier
opaque-enum-declaration
:
enum-key
attribute-specifier-seq
opt optenum-head-name
enum-base
;
enum-key
:
enum
enum class
enum struct
enum-base
:
:
type-specifier-seq
enumerator-list
:
enumerator-definition
enumerator-list
,
enumerator-definition
enumerator-definition
:
enumerator
enumerator
=
constant-expression
enumerator
:
identifier
attribute-specifier-seq
Rozhodnout
Využití
// unscoped enum:
// enum [identifier] [: type] {enum-list};
// scoped enum:
// enum [class|struct] [identifier] [: type] {enum-list};
// Forward declaration of enumerations (C++11):
enum A : int; // non-scoped enum must have type specified
enum class B; // scoped enum defaults to int but ...
enum class C : short; // ... may have any integral underlying type
Parametry
identifier
Název typu zadaný výčtu.
type
Základní typ výčtů; všechny enumerátory mají stejný základní typ. Může být jakýkoli celočíselný typ.
enum-list
Čárkami oddělený seznam výčtů v výčtu. Každý název enumerátoru nebo proměnné v oboru musí být jedinečný. Hodnoty však mohou být duplikovány. V neskopovaném výčtu je rozsah okolního rozsahu; v výčtu s vymezeným oborem je enum-list
samotný obor. V výčtu s vymezeným oborem může být seznam prázdný, který v důsledku definuje nový celočíselný typ.
class
Pomocí tohoto klíčového slova v deklaraci zadáte výčt je vymezen a identifier
musí být zadán. Klíčové slovo můžete použít struct
také místo class
, protože jsou v tomto kontextu sémanticky ekvivalentní.
Rozsah enumerátoru
Výčet poskytuje kontext pro popis rozsahu hodnot, které jsou reprezentovány jako pojmenované konstanty. Tyto pojmenované konstanty se také nazývají enumerátory. V původních typech jazyka C a C++ enum
jsou nekvalifikované enumerátory viditelné v celém oboru, ve kterém enum
je deklarován. V vymezených výčtech musí být název enumerátoru kvalifikovaný názvem enum
typu. Následující příklad ukazuje tento základní rozdíl mezi dvěma druhy výčtů:
namespace CardGame_Scoped
{
enum class Suit { Diamonds, Hearts, Clubs, Spades };
void PlayCard(Suit suit)
{
if (suit == Suit::Clubs) // Enumerator must be qualified by enum type
{ /*...*/}
}
}
namespace CardGame_NonScoped
{
enum Suit { Diamonds, Hearts, Clubs, Spades };
void PlayCard(Suit suit)
{
if (suit == Clubs) // Enumerator is visible without qualification
{ /*...*/
}
}
}
Každému názvu v výčtu je přiřazena integrální hodnota, která odpovídá jeho místu v pořadí hodnot v výčtu. Ve výchozím nastavení je první hodnota přiřazena 0, další hodnota je přiřazena 1 atd., ale můžete explicitně nastavit hodnotu enumerátoru, jak je znázorněno tady:
enum Suit { Diamonds = 1, Hearts, Clubs, Spades };
Výčtu Diamonds
je přiřazena hodnota 1
. Další enumerátory, pokud nemají explicitní hodnotu, obdrží hodnotu předchozího enumerátoru plus jeden. V předchozím příkladu Hearts
by měla hodnotu 2, Clubs
měla by hodnotu 3 atd.
Každý enumerátor je považován za konstantu a musí mít jedinečný název v rámci oboru, ve kterém enum
je definován (pro neskopované výčty) nebo uvnitř enum
samotného (pro výčty s vymezeným oborem). Hodnoty zadané názvů nemusí být jedinečné. Představte si například tuto deklaraci neskopovaného výčtu Suit
:
enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };
Hodnoty Diamonds
, , Hearts
Clubs
a Spades
jsou 5, 6, 4 a 5, v uvedeném pořadí. Všimněte si, že 5 se používá více než jednou; to je povoleno, i když to nemusí být zamýšleno. Tato pravidla jsou stejná pro výčty s vymezeným oborem.
Přetypování pravidel
Neskopované výčtové konstanty lze implicitně převést na int
, ale int
nikdy není implicitně konvertibilní na hodnotu výčtu. Následující příklad ukazuje, co se stane, když se pokusíte přiřadit hand
hodnotu, která není Suit
:
int account_num = 135692;
Suit hand;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
Přetypování je nutné k převodu int
na vymezený nebo neskopovaný enumerátor. Můžete však upřednostnit neskopovaný enumerátor na celočíselnou hodnotu bez přetypování.
int account_num = Hearts; //OK if Hearts is in an unscoped enum
Použití implicitních převodů tímto způsobem může vést k nezamýšleným vedlejším účinkům. Chcete-li odstranit programovací chyby spojené s neskopovanými výčty, jsou oborové hodnoty výčtu silného typu. Výčty s vymezeným oborem musí být kvalifikované názvem typu výčtu (identifikátor) a nelze je implicitně převést, jak je znázorněno v následujícím příkladu:
namespace ScopedEnumConversions
{
enum class Suit { Diamonds, Hearts, Clubs, Spades };
void AttemptConversions()
{
Suit hand;
hand = Clubs; // error C2065: 'Clubs' : undeclared identifier
hand = Suit::Clubs; //Correct.
int account_num = 135692;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
hand = static_cast<Suit>(account_num); // OK, but probably a bug!!!
account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int'
account_num = static_cast<int>(Suit::Hearts); // OK
}
}
Všimněte si, že řádek hand = account_num;
stále způsobuje chybu, ke které dochází u neskopovaných výčtů, jak je znázorněno výše. Je povoleno explicitním přetypováním. Nicméně, s vymezenými výčty, pokus o převod v dalším příkazu , account_num = Suit::Hearts;
již není povolen bez explicitního přetypování.
Výčty bez výčtů
Visual Studio 2017 verze 15.3 a novější (k dispozici s /std:c++17
a novější): Definováním výčtu (běžný nebo vymezený) s explicitním podkladovým typem a bez výčtů můžete v důsledku zavést nový integrální typ, který nemá implicitní převod na žádný jiný typ. Použitím tohoto typu místo předdefinovaného základního typu můžete eliminovat potenciál drobných chyb způsobených neúmyslnými implicitními převody.
enum class byte : unsigned char { };
Nový typ je přesná kopie základního typu, a proto má stejnou konvenci volání, což znamená, že se dá použít napříč abI bez jakéhokoli snížení výkonu. Při inicializaci proměnných typu pomocí inicializace direct-list není vyžadováno přetypování. Následující příklad ukazuje, jak inicializovat výčty bez výčtů v různých kontextech:
enum class byte : unsigned char { };
enum class E : int { };
E e1{ 0 };
E e2 = E{ 0 };
struct X
{
E e{ 0 };
X() : e{ 0 } { }
};
E* p = new E{ 0 };
void f(E e) {};
int main()
{
f(E{ 0 });
byte i{ 42 };
byte j = byte{ 42 };
// unsigned char c = j; // C2440: 'initializing': cannot convert from 'byte' to 'unsigned char'
return 0;
}
Viz také
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro