Одинаковые члены в ограниченном и неограниченном перечислениях

Рейтинг: 2Ответов: 2Опубликовано: 21.02.2023
enum UnscopedColor { red, green, blue };
enum class ScopedColor { red, green, blue };

int main() {
  using enum ScopedColor;
}

Какой из енумов будет использоваться и почему так? Как использовать определённый?

Ответы

▲ 3

Как известно,

enum UnscopedColor { red, green, blue };

не только определяет тип UnscopedColor, но и добавляет три идентификатора red, green и blue в глобальную область видимости.

Это означает, что в коде мы можем написать red и ожидать, что получим значение именно идентификатора red из перечисления UnscopedColor (т.е. 0). Правда ровно до тех пор, пока кто-нибудь не добавит в локальную область видимости новый идентификатор red, который перекроет red из глобальной области видимости. Есть много способов это сделать.

Например просто создать переменную с именем red:

#include <iostream>

enum UnscopedColor { red, green, blue };

int main() {
  std::cout << red << std::endl; // это red = 0 из UnscopedColor
  auto red = 42;
  std::cout << red << std::endl; // а это наша новая локальная переменная red = 42
}

Или вытащить идентификатор red из каких-то глубин:

#include <iostream>

enum UnscopedColor { red, green, blue };

namespace A {
  namespace B {
    constexpr const char red[] = "red";
  }
}

int main() {
  std::cout << red << std::endl; // это red = 0 из UnscopedColor
  using A::B::red;
  std::cout << red << std::endl; // угадай кто здесь
}

В целом это достаточно универсальный механизм в C++ когда более новые идентификаторы вводятся в локальную область видимости и перекрывают старые (пока область видимости не закончится).

Тем не менее есть способ (не всегда, но есть) достучаться да скрытых идентификаторов. Например тех, которые объявлены в глобальной области видимости. Достаточно использовать префикс ::. Т.е. в наших примерах мы можем писать ::red и быть уверенными, что это будет именно идентификатор из UnscopedColor, а не откуда-то еще.

Возвращаясь к оригинальному коду,

enum UnscopedColor { red, green, blue };
enum class ScopedColor { red, green, blue };

int main() {
  using enum ScopedColor;
}

думаю уже стало понятно какой enum "будет использоваться". Хотя это не совсем корректный вопрос. Т.к. using enum ничего не делает с самим типом ScopedColor, а только вносит имена red, green и blue типа ScopedColor в локальную область видимости (и естественно перекрывает старые идентификаторы из UnscopedColor). И если бы в ScopedColor отсутствовал blue, то в локальной области видимости мы бы получили прекрасный микс: red и green были бы типа ScopedColor, а blue принадлежал бы UnscopedColor. Ну лучше так не делать разумеется.

▲ -1

В данном случае будет использоваться enum class ScopedColor, потому что он объявлен с ключевым словом class, что означает, что имена перечислений будут принадлежать области видимости этого перечисления.