Почему это выражение является неподписанным?

В x86_64 CentOS 7 GCC 4.8.5 С++ 11:

#include <iostream>

int main()
{
    std::cout << ((ssize_t)1 - (size_t)5) << '\n';
}

// Output: 18446744073709551612

Но:

#include <iostream>

int main()
{
    std::cout << ((ssize_t)1 - (unsigned int)5) << '\n';
}

// Output: -4

И на i686 CentOS 6 GCC 4.8.2 С++ 11 оба они дают 4294967292, поэтому я должен это сделать:

#include <iostream>

int main()
{
    std::cout << ((ssize_t)1 - (ssize_t)5) << '\n';
}

// Output: -4

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

Что именно представляет собой последовательность стандартных правил, которая приводит меня к этим результатам?

Ответы

Ответ 1

Отказ от ответственности: я ссылаюсь на пункт 11 пункта 5 последнего проекта N4606 С++ 17. Текст, который я цитирую и цитирую, содержится в абзаце 9 из N3337, который практически идентичен стандарту С++ 11, и в этой форме также в FD С++ 14, поэтому этот ответ также относится к этим стандартам.

Предполагая, что ssize_t и size_t имеют одинаковый ранг, в вашем первом случае [expr]/(11.5.5) применяется:

В противном случае оба операнда должны быть преобразованы в целочисленный тип без знака, соответствующий тип операнда со знаком целочисленного типа.

1 будет преобразован в неподписанную версию ssize_t, которая должна быть size_t — следовательно, неподписанное нижнее течение и значение 2 sizeof(size_t)*8 -4.

Для вашего второго случая, считая, что ранг unsigned меньше, чем ранг ssize_t, а последний может содержать все прежние значения; см. [expr]/(11.5.4):

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

т.е. 5 будет преобразован в ssize_t, и мы получим отрицательный результат. Если ssize_t не имеет большего ранга, чем unsigned, мы получим 2 sizeof(unsigned)*8 -4; если вместо ssize_t не удалось сохранить все значения unsigned, мы снова получим отрицательный результат, потому что мы переходим к вышесказанному (11.5.5).