Подтвердить что ты не робот

Вычитание двух знаков без знака

У меня есть следующий фрагмент кода:

#include <cstdint>

template <typename T>
T test(T a, T b)
{
    float aabb = reinterpret_cast<float>(a - b);
}

int main(int argc, const char *argv[])
{
    std::uint8_t a8, b8;
    test(a8, b8);
    return 0;
}

Я знаю, что reinterpret_cast<float> не может работать и что он дает ошибку во время компиляции. Я использую эту ошибку, поэтому компилятор сообщает мне тип a - b.

Проблема в том, что в этом случае он говорит, что тип a - b равен int, если оба они uint8_t (unsigned char). То же самое происходит с uint16_t. Но не с uint32_t, который говорит, что a - b есть unsigned int.

Итак, мой вопрос: это намеренное поведение (что unsigned char - unsigned char дает int), или это какая-то странная ошибка компилятора (проверена как с GCC, так и с clang)?

4b9b3361

Ответ 1

Да, это ожидается, как часть так называемых обычных арифметических преобразований в сочетании с правилами для целенаправленного продвижения.

Точная формулировка изменилась между С++ 03 и С++ 11, но в этом случае конечный результат будет таким же.


[C++03: 4.5/1]: Значение r char, signed char, unsigned char, short int или unsigned short int может быть преобразовано в rvalue типа int, если int может представлять все значения тип источника; в противном случае исходное значение rvalue может быть преобразовано в rvalue типа unsigned int.

[C++03: 4.5/5]: Эти преобразования называются интегральными акциями.

[C++03: 5/9]: Многие двоичные операторы, ожидающие операндов арифметики или типа перечисления, вызывают конверсии и аналогично вызывают типы результатов. Цель состоит в том, чтобы дать общий тип, который также является типом результата.

Этот шаблон называется обычным арифметическим преобразованием, которое определяется следующим образом:

  • Если любой из операндов имеет тип long double, другой должен быть преобразован в long double.
  • В противном случае, если любой операнд double, другой должен быть преобразован в double.
  • В противном случае, если любой операнд float, другой должен быть преобразован в float.
  • В противном случае интегральные акции (4.5) должны выполняться на обоих операндах. 54
  • Затем, если любой операнд unsigned long, другой должен быть преобразован в unsigned long.
  • В противном случае, если один операнд является long int, а другой unsigned int, то если a long int может представлять все значения unsigned int, unsigned int преобразуется в long int; в противном случае оба операнда должны быть преобразованы в unsigned long int.
  • В противном случае, если любой операнд long, другой должен быть преобразован в long.
  • В противном случае, если любой операнд unsigned, другой должен быть преобразован в unsigned.

[Примечание: в противном случае единственным оставшимся случаем является то, что оба операнда int]


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

[C++11: 4.5/7]: Эти преобразования называются интегральными акциями.

[C++11: 5.9]: Многие двоичные операторы, ожидающие операндов арифметики или типа перечисления, вызывают конверсии и аналогично вызывают типы результатов. Цель состоит в том, чтобы дать общий тип, который также является типом результата.

Этот шаблон называется обычным арифметическим преобразованием, которое определяется следующим образом:

  • Если либо операнд имеет тип перечисления с областью (7.2), конверсии не выполняются; если другой операнд не имеет одного и того же типа, выражение плохо сформировано.
  • Если один из операндов имеет тип long double, другой должен быть преобразован в длинный double.
  • В противном случае, если один из операндов является двойным, другой должен быть преобразован в double.
  • В противном случае, если любой операнд является float, другой должен быть преобразован в float.
  • В противном случае интегральные акции (4.5) должны выполняться на обоих операндах. 59 Затем к продвинутым операндам должны применяться следующие правила:
    • Если оба операнда имеют один и тот же тип, дальнейшее преобразование не требуется.
    • В противном случае, если оба операнда имеют целочисленные типы или оба имеют неподписанные целые типы, операнд с типом ранга меньшего целочисленного преобразования должен быть преобразован в тип операнда с большим рангом.
    • В противном случае, если операнд с беззнаковым целочисленным типом имеет ранг, больший или равный рангам типа другого операнда, операнд со знаком целочисленного типа должен быть преобразован в тип операнда с целым числом без знака.
    • В противном случае, если тип операнда со знаком целочисленного типа может представлять все значения типа операнда с целым типом без знака, операнд с целым типом без знака должен быть преобразован в тип операнда со знаком целого числа тип.
    • В противном случае оба операнда должны быть преобразованы в целочисленный тип без знака, соответствующий типу операнда со знаком целочисленного типа.