Неправильное назначение значений в char перечислении
Я играл с перечислениями и пытался воспроизвести некоторые примеры с этой страницы. Первоначальные примеры работали по назначению, однако я получил несколько интересных результатов со следующим кодом:
#include <iostream>
enum num : char {
zero = '0',
one = '1',
two = '2',
three = '3',
four = '4',
five = '5',
six = '6'
};
int main()
{
const char two = '2';
std::cout << two << std::endl;
std::cout << num::two;
return 0;
}
Вывод:
2
50
Я ожидал, что оба результата будут одинаковыми, но num::two
, похоже, напечатает другое значение. Также это значение не меняет (50)
, поэтому я предполагаю, что это не случайное/мусорное значение, и есть какой-то метод char/int, который я не понимаю? Вот ссылка ideone.
Я знаю, что я могу заставить его работать, назначив таким образом zero = 0
, без одинарных кавычек, и он работает. Тем не менее, я хочу знать, что происходит за кулисами, и как я могу контролировать то, что не единственное значение цифр, которое я могу распечатать с помощью одинарных кавычек.
Ответы
Ответ 1
Теперь это действительно перейдет к перегрузке char
; К сожалению, ни один из компиляторов не выпускает DR 1601.
[conv.prom]/4:
Значение неперечисленного типа перечисления, базовый тип которого fixed ([dcl.enum]) может быть преобразован в prvalue его базового тип.
Это означает, что num
можно повысить до char
.
Более того, если интегральное продвижение может быть применено к его основополагающим type, prvalue неперечисленного типа перечисления, базовый тип которого фиксируется, также может быть преобразовано в prvalue продвинутого базового тип.
Так что num
может быть увеличено до int
.
Соответствующими кандидатами являются:
template <class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,
char ch );
template<class charT, class Traits>
basic_ostream<charT, Traits>& basic_ostream<charT, Traits>::operator<<(int);
Для обоих кандидатов первым аргументом является преобразование идентичности, а второе - продвижение. Оба num
до char
и num
до int
имеют рейтинг продвижения.
Pre-DR1601, они одинаково хороши, поэтому входит шаблон/не шаблонный тай-брейк. Первый - это шаблон функции; вторая - простая функция-член, поэтому выигрывает вторая.
В DR1601 добавлено правило, в котором говорится:
Преобразование, которое способствует перечислению, базовый тип которого привязанный к его базовому типу, лучше, чем тот, который способствующий базовому типу, если они отличаются друг от друга.
Это означает, что num
до char
теперь лучше, чем num
до int
, поэтому первая перегрузка теперь лучше соответствует и должна быть выбрана.
Ответ 2
В соответствии со стандартом С++ (4.5 Интегральные акции)
4 Значение класса неперечисленного перечисления, базовый тип которого фиксированное (7.2) может быть преобразовано в prvalue своего базового типа. Кроме того, если интегральное продвижение может быть применено к его базовому типу, prvalue неперечисленного типа перечисления, базовый тип которого фиксирован, также может быть преобразован в prvalue продвинутого базового типа Тип.
Таким образом, применяется интегральное продвижение, и оператор < для объектов типа int.
Ответ 3
Когда вы говорите enum num : char
, вы выражаете тот факт, что num
внутренне реализуется в терминах char
, но все равно может быть автоматически преобразован в целочисленное значение, что необязательно char
.
В качестве страницы, которую вы цитируете, говорится:
Значения неперечисленного типа перечисления неявно-конвертируются в интегральные типы.
См. Почему значение перечисления с фиксированным базовым типом char разрешено для fct (int) вместо fct (char)? для интересное обсуждение проблем стандартной формулировки С++ в отношении сочетания целостного продвижения и фиксированных базовых типов.
В любом случае вы можете представить все это как класс с частной переменной члена char
и общедоступным оператором преобразования int
:
// very similar to your enum:
class num {
private:
char c;
public:
num(char c) : c(c) {}
operator int() const {
return c;
}
};
num two { '2' };
std::cout << two; // prints 50
Чтобы повысить безопасность типа и сделать строку std::cout
ошибкой компиляции, просто переведите enum
в enum class
:
enum class num : char
Это снова похоже на воображаемое class num
выше, но без оператора преобразования.
Когда вы подаете экземпляр num
в std::cout
, вы являетесь клиентом num
и логически не должны полагать, что выходной формат будет учитывать внутреннюю реализацию char
.
Чтобы получить больший контроль над выходным форматом, вы должны сделать это с любым другим настраиваемым типом и перегрузить operator<<
для std::ostream
. Пример:
#include <iostream>
enum class num : char {
zero = '0',
one = '1',
two = '2',
three = '3',
four = '4',
five = '5',
six = '6'
};
std::ostream& operator<<(std::ostream& os, num const& n)
{
switch (n)
{
case num::zero: os << "Zero"; break;
case num::one: os << "One"; break;
case num::two: os << "Two"; break;
case num::three: os << "Three"; break;
// and so on
}
return os;
}
int main()
{
std::cout << num::two; // prints "Two"
}
Конечно, конкретные значения char
экземпляров enum теперь стали бесполезными, поэтому вы можете полностью избавиться от них:
enum class num : char {
zero,
one,
two,
three,
four,
five,
six
};
Это может показаться вам странным, но имейте в виду, что перечисление, которое не представляет ничего, кроме общих чисел от нуля до шести, не является реалистичным вариантом использования.
Ответ 4
Поскольку два вызова двух разных перегрузок операторов:
-
первый вызывает нечлен operator<<
для std::ostream
и char
. Это печатает символ.
-
Второй пример вызывает член operator<<
для int
из-за целых рекламных акций, как объясняется другими ответами.
Ответ 5
Причина в том, что ваш enum : char
не совпадает с char
(это именно то, что мы хотим - мы не хотим, чтобы перечисление было таким же, как и другие типы, даже если они совместимы с назначением - мы хотим void func(num n)
быть отличным от void func(char n)
, справа?).
Итак, поскольку enum num
не является char
, будет использоваться operator<<(int)
и печатает целочисленное значение, даже если базовый тип char
. Не совсем разумно, но я уверен, что это происходит.