Почему "авто" не уважает унарный минус-оператор?
Я новичок в 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
который допускает отрицательные значения.