Почему "авто" не уважает унарный минус-оператор?

Я новичок в C++, но я нахожу это поведение auto странным:

class A{};

int main() {
    A a;
    auto x = -(sizeof(a));
    cout << x << endl;
    return 0;
}

Переменная x в этом случае unsigned но я использовал унарный минус-оператор при инициализации переменной. Как получилось, что рассматривается только возвращаемый тип sizeof (std::size_t), но не тот факт, что сохраненный номер будет отрицательным из-за используемого оператора?

Я знаю, что size_t является неподписанным int.

Я пробовал это с GCC 8.1.0 и C++ 17.

Ответ 1

Реальная проблема здесь заключается в том, что использование унарного оператора минус, как и все остальные встроенные арифметические операторы, является предметом целостных рекламных акций. Настолько удивительно, что результат применения унарного минуса к size_t будет по-прежнему size_t и нет необходимости винить auto.

Встречный пример. В этом случае из-за интегральных продвижений тип x будет int поэтому выход будет -1:

unsigned short a{1};
auto x{-a};
cout << x << endl;

Ответ 2

Ваше выражение -(sizeof(a)) применяет унарный - оператор на значение без знака типа. Унарный оператор не превращает целое значение без знака в подписанное; он скорее определяет, какое непознанное значение будет являться результатом такой операции следующим образом (ср. унарные арифметические операторы на cppreference.com):

Встроенный унарный минус-оператор вычисляет отрицание своего продвинутого операнда. Для unsigned a значение -a равно 2 ^ b -a, где b - количество бит после продвижения по службе.

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

Ответ 3

Результат (унарный) - применяется к неподписанному значению без знака, а sizeof возвращает значение без знака.

Операнд унарного оператора должен иметь арифметический или неперечисленный тип перечисления, а результат - отрицание его операнда. Интегральное продвижение выполняется на интегральных или перечисляющих операндах. Отрицание беззнакового количества вычисляется путем вычитания его значения из 2 ^ n, где n - количество бит в продвинутом операнде. Тип результата - тип продвинутого операнда.

[expr.unary.op]

Результат sizeof и sizeof... является константой типа std​::​size_t

[expr.sizeof]

Чтобы избежать поведения, определенного при реализации, вам необходимо преобразовать его в int перед применением -

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

[conv.integral]

class A{};

int main() {
    A a;
    auto x = -(int{sizeof(a)});
    cout << x << endl;
    return 0;
}

Ответ 4

Если мы посмотрим на: https://en.cppreference.com/w/cpp/language/sizeof, результат будет иметь тип size_t который не имеет unsigned. Вам необходимо объявить его как signed int чтобы разрешить отрицательные значения.

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