Я не очень знаком с компилятором Clang, так как я привык к Visual Studio.В настоящее время я использую Visual Studio 2017. Мне удалось скомпилировать и запустить ваш код с языковым флагом, установленным как для c ++ 14, так и для c ++ 17 в сборках отладки x86 и x64.Вместо того, чтобы возвращать добавление в вашем примере:
return d + B;
Я решил вывести их на консоль:
std::cout << (d + B);
и во всех 4 случаях мой компиляторвывел значение 11
.
Я тоже не уверен насчет GCC, так как я не пробовал это на вашем примере, но это наводит меня на мысль, что это зависит от компиляторавыпуск.
Я перешел по вашей ссылке и прочитал раздел 8, на который вы ссылались, но то, что привлекло мое внимание из этого черновика, это подробности из других разделов, а именно 7 и 10.
Раздел 7 сообщает:
Для перечисления, базовый тип которого не фиксирован, базовый тип является целочисленным типом, который можетпредставляют все значения перечислителя, определенные в перечислении.Если ни один целочисленный тип не может представлять все значения перечислителя, перечисление неверно сформировано.Определяется реализацией, какой интегральный тип используется в качестве базового типа, за исключением того, что базовый тип не должен быть больше, чем int, если только значение перечислителя не может поместиться в int или unsigned int.Если список-перечислитель пуст, базовый тип выглядит так, как если бы перечисление имело единственный перечислитель со значением 0.
Но именно это предложение или предложение привлекли мое внимание:
Определяется реализацией, какой целочисленный тип используется в качестве базового типа, за исключением того, что базовый тип не должен быть больше, чем int, если значение перечислителя не может поместиться в int или unsigned int.
Раздел 10 сообщает:
Значение перечислителя или объекта типа перечисления с незаданной областью преобразуется в целое числоинтегральное продвижение.[Пример:
enum color { red, yellow, green=20, blue };
color col = red;
color* cp = &col;
if (*cp == blue) // ...
делает цвет типом, описывающим различные цвета, а затем объявляет col как объект этого типа и cp как указатель на объект этого типа.Возможные значения объекта типа color: красный, желтый, зеленый, синий;эти значения могут быть преобразованы в целочисленные значения 0, 1, 20 и 21. Поскольку перечисления являются разными типами, объектам типа color могут быть назначены только значения типа color.
color c = 1; // error: type mismatch, no conversion from int to color
int i = yellow; // OK: yellow converted to integral value 1, integral promotion
Обратите внимание, что это неявное преобразование перечисления в int не предусмотрено для перечисления с областью действия:
enum class Col { red, yellow, green };
int x = Col::red; // error: no Col to int conversion
Col y = Col::red;
if (y) { } // error: no Col to bool conversion
- конец примера]
Именно этидве строки, которые привлекли мое внимание:
color c = 1; // error: type mismatch, no conversion from int to color
int i = yellow; // OK: yellow converted to integral value 1, integral promotion
Итак, давайте вернемся к вашему примеру:
enum A {B = 3, C = 7};
int main() {
A d = static_cast<A>(8);
return d + B;
}
Здесь A
- полный тип, B
& C
- это перечислители, которые оцениваются как постоянное выражение целочисленного типа путем повышения и устанавливаются в значения 3
и 7
соответственно.Это касается объявления enum A{...};
Внутри main()
вы теперь объявляете экземпляр или объект A
с именем d
, поскольку A
является полным типом.Затем вы присваиваете d
значение 8
, которое является константным выражением или константным литералом через механизм static_cast
.Я не уверен на 100%, выполняет ли каждый компилятор static_cast
одинаково точно или нет;Я не уверен, зависит ли это от компилятора.
Итак, d
- это объект типа A
, но поскольку значение 8
отсутствует в списке перечислений, я считаю, что оно подпадает под условие implementation defined
.Это должно затем привести d
к целочисленному типу.
Затем в вашем последнем утверждении, в котором вы возвращаете d+B
.
Leскажем, что d
был преобразован в целочисленный тип со значением 8
, затем вы добавляете перечисляемое значение B
, которое составляет 3
к 8
, и, следовательно, вы должны получить вывод 11
в котором у меня есть все 4 моих тестовых случая на visual studio.
Теперь, что касается вашего компилятора с Clang, я не могу сказать, но, насколько я могу судить, это, похоже, не вызывает каких-либо ошибок.или неопределенное поведение по крайней мере в соответствии с Visual Studio.Опять же, поскольку этот код, по-видимому, определяется реализацией, я думаю, что он сильно зависит от вашего конкретного компилятора и его версии, а также от языковой версии, под которой вы его компилируете.
Я не могу сказать, что это полностью ответит на ваш вопрос, но, возможно, это позволит пролить некоторое представление на подчеркивающую работу компиляторов в соответствии с документацией проектов и стандартов.
-Edit-
Я решил запустить это через мой отладчик, и я поставил точку останова в этой строке:
A d = static_cast<A>(8);
ЗатемЯ прошел через выполнение этой строки кода и посмотрел на значение в отладчике.Здесь, в Visual Studio, d
имеет значение 8
.Однако под его типом он указан как A
, а не int
.Так что я не знаю, продвигает ли это его до int
или нет, или это может произойти при оптимизации компилятора, что-то скрытое, например asm
, которое рассматривает d
как int
илиunsigned int
и т.д .;но Visual Studio позволяет мне присваивать целочисленное значение через static_cast
перечисляемому типу.Однако, если я удаляю static_cast
, он не скомпилируется, заявив, что вы не можете присвоить тип int
типу A
.
Это заставляет меня поверить, что мое первоначальное утверждение выше действительно неверноили только частично правильно.Компилятор не полностью «переводит» его в целочисленный тип при присваивании, так как d
по-прежнему остается экземпляром A
, если только он не делает это под капотом, о котором я не знаю.
Я еще не проверил, чтобы увидеть asm
этого кода, чтобы увидеть, какие инструкции по сборке генерируются Visual Studio ... поэтому в настоящее время я не могу провести полную оценку на данный момент.Теперь, позже, если у меня будет больше свободного времени;Я могу заглянуть в него, чтобы увидеть, какие строки asm
генерирует мой компилятор, чтобы увидеть основные действия, выполняемые компилятором.