Что вызывает преобразование этого подписанного int в unsigned int с использованием троичного, но не шорт? - PullRequest
2 голосов
/ 12 февраля 2020

При использовании троичного оператора в инициализации списка, что вызывает неявное преобразование int в unsigned int (и аналогично для long long), но не short в unsigned short (и аналогично для char) .

В частности, я удивлен, что функция i32v2 компилируется нормально, а другие нет:

unsigned short f16(unsigned short x);
unsigned int f32(unsigned int x);

void i16(short value) {
    unsigned short encoded{value}; // narrowing, makes sense
}

void i32(int value) {
    unsigned int encoded{value}; // narrowing, makes sense
}

void i16v2(short value) {
    unsigned short encoded{false ? value : f16(value)}; // narrowing, makes sense
}

void i32v2(int value) {
    unsigned int encoded{false ? value : f32(value)}; // not narrowing, huh?
}

Полный пример здесь: https://godbolt.org/z/fVTcrr

Я предполагаю, что троичный оператор неявно преобразует int в unsigned int, но я не понимаю, почему он не может преобразовать short в unsigned short аналогичным образом.

Я ожидаю, если это было возможно для int, тогда троичный оператор должен также быть в состоянии преобразовать любой другой тип signed в unsigned, когда это возможно:

Если тип назначения не подписан, результирующее значение является наименьшим значением без знака, равным исходному значению по модулю 2n, где n - количество битов, используемых для представления типа назначения.

(https://en.cppreference.com/w/cpp/language/implicit_conversion)

Может кто-нибудь объяснить это бех avior, и, если возможно, ссылаться на стандартную или соответствующую страницу cppreference?

Ответы [ 2 ]

3 голосов
/ 12 февраля 2020

Стандарт гласит (цитаты из последней версии):

[expr.cond]

Lvalue-to-значение, массив-указатель и функция-к- Стандартные преобразования указателя выполняются на втором и третьем операндах. После этих преобразований должно выполняться одно из следующего:

  • Второй и третий операнды имеют одинаковый тип; ... [не применяется]

  • Второй и третий операнды имеют арифметику c [применяется] или тип перечисления; обычные арифметические c преобразования выполняются , чтобы привести их к общему типу, и результат этого типа.

  • ...

[expr.arith.conv]

Многие бинарные операторы, которые ожидают операнды арифметического c или типа перечисления, вызывают преобразования и выдают типы результата аналогичным образом. Цель состоит в том, чтобы получить общий тип, который также является типом результата. Этот шаблон называется обычными арифметическими c преобразованиями, которые определяются следующим образом:

  • Если любой из операндов имеет тип перечисления с ограниченным диапазоном ... [не применяется]

  • Если один из операндов имеет тип long double ... [не применяется]

  • В противном случае, если любой из операндов является двойным ... [не применяется]

  • В противном случае, если любой из операндов является плавающим ... [не применяется]

  • В противном случае интегральные преобразования ([conv.prom]) должны выполняться для обоих операндов . Затем к повышенным операндам применяются следующие правила:

...

[conv.prom]

Значение типа integer кроме bool, char16_t, char32_t или wchar_t [применяется] , чей целочисленный ранг преобразования ([conv.rank]) меньше, чем ранг int [применяется] можно преобразовать в значение типа int, если int может представлять все значения типа источника [очевидно, применяется 1 ] ; в противном случае исходное значение prvalue может быть преобразовано в значение типа unsigned int.

Эти преобразования называются интегральными повышениями.

Так, в случае i16v2, второе и третьи операнды short и unsigned short. Оба, очевидно, 1 повышаются до int в вашей системе, и результат int условного оператора затем используется для инициализации unsigned short.

В случае i32v2, промо-акции не применяются, и общий тип int и unsigned int равен unsigned int.

1 Я говорю очевидно , потому что технически unsigned short может повышаться до unsigned int в некоторой системе exoti c, где их размер одинаков, и в этом случае int не может представлять все значения unsigned short. Результат, который вы наблюдаете, показывает, что это не относится к вашей системе, чего и следовало ожидать.

2 голосов
/ 12 февраля 2020

Обратите внимание, что для false ? value : f16(value), integra_promotion выполняется сначала для операндов. Для арифметического c оператора ,

Если операнд, переданный оператору арифметического c, является целочисленным или незаданным типом перечисления, то перед любым другим действием (но после lvalue- преобразование в значение, если применимо), операнд подвергается интегральному продвижению .

и

Следующие неявные преобразования классифицируются как интегральные продвижения:

  • signed char или signed short можно преобразовать в int;

Это означает, что тип возвращаемого значения false ? value : f16(value) равен int, а затем вызывает сужающее преобразование в unsigned short.

С другой стороны, тип возвращаемого значения, т.е. общий тип для false ? value : f32(value), равен unsigned int, тогда unsigned int encoded{false ? value : f32(value)}; в порядке.

В противном случае операнд имеет целочисленный тип (потому что bool, char, char8_t, char16_t, char32_t, wchar_t и перечисление с незаданной областью были повышены на этом этапе), и для получения общего типа применяются целочисленные преобразования следующим образом:

  • .. .
  • В противном случае, если ранг преобразования беззнакового операнда больше или равен рангу преобразования подписанного операнда, знаковый операнд преобразуется в тип беззнакового операнда.

Для long или long long они не будут повышены до int, тогда у них нет таких судится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...