Ответ 1
+
интерпретируется как унарный плюс. Он просто возвращает значение продвинутого своего операнда.
У меня была странная ошибка в моей программе, и после нескольких часов отладки я нашел следующую очень тупую строку:
int a = b * (c * d * + e)
Если вы этого не видите: между d
и e
я написал * +
, где был предназначен только a +
.
Почему эта компиляция и что она на самом деле означает?
+
интерпретируется как унарный плюс. Он просто возвращает значение продвинутого своего операнда.
Как объяснил Брайан, он возвращает продвинутое значение.
Если это было -
, оно вернет отрицание:
int a = 5;
int b = 6;
unsigned int c = 3;
std::cout << (a * +b); // = 30
std::cout << (a * -b); // = -30
std::cout << (1 * -c); // = 4294967293 (2^32 - 3)
Это компилируется, потому что +
интерпретируется как унарный плюс, который будет выполнять интегральные продвижения по интегральным или перечисляемым типам, и результат будет иметь тип продвигаемого операнда.
Предполагая, что e
является интегральным или неперечисленным типом перечисления, в конечном итоге будет иметь интегральные поощрения, поскольку *
применяет обычные арифметические преобразования к его операндам, которые заканчиваются интегральными акциями для интегральных типов.
Из проекта стандарта С++ 5.3.1
[expr.unary.op]:
Операнд унарного + оператора должен иметь арифметическое, неперечисленное перечисление или тип указателя, а результатом является значение аргумента. Интегральное продвижение выполняется на интегральных или перечисляющих операндах. Тип результата - тип продвинутого операнда.
Интегральные рекламные акции рассматриваются в разделе 4.5
[conv.prom], и если переменные e
являются типом, отличным от bool, char16_t, char32_t, or wchar_t
и имеют ранг преобразования меньше, чем int, то он будет охватываться абзацем 1
Значение целочисленного типа, отличного от bool, char16_t, char32_t или wchar_t, целочисленное преобразование rank (4.13) меньше ранга int может быть преобразован в prvalue типа int, если int может представлять все значения типа источника; в противном случае исходное значение prvalue может быть преобразовано в prvalue типа unsigned внутр.
Для полного набора случаев мы можем посмотреть cppreference.
Унарный плюс также может быть полезен в некоторых случаях для устранения двусмысленности, интересным случаем будет Разрешение двусмысленной перегрузки на указатель на функцию и std:: function для лямбды с использованием +.
Обратите внимание, что для этих ответов, относящихся к унарным -
и отрицательным значениям, это вводит в заблуждение, как показывает этот пример:
#include <iostream>
int main()
{
unsigned x1 = 1 ;
std::cout << -x1 << std::endl ;
}
что приводит к:
4294967295
Смотрите live используя gcc на wandbox.
Интересно отметить, что унарный плюс был добавлен в C99 для симметрии с унарным минусом, из Обоснование для языков международного стандартного программирования-C:
Унарный плюс был принят Комитетом C89 из нескольких реализаций, для симметрии с унарным минусом.
и я не могу придумать хороший случай, когда кастинг будет недостаточным для достижения того же желаемого продвижения/конверсии. Пример lambda, который я цитирую выше, используя унарный плюс, чтобы заставить лямбда-выражение быть преобразованным в указатель функции:
foo( +[](){} ); // not ambiguous (calls the function pointer overload)
может быть выполнено с использованием явного приведения:
foo( static_cast<void (*)()>( [](){} ) );
и можно утверждать, что этот код лучше, поскольку намерение явно.
Стоит отметить, что Аннотированное справочное руководство по С++ (ARM) содержит следующий комментарий:
Унарный плюс - историческая авария и вообще бесполезен.
Как они объяснили, (+) и (-) были просто использованы как унарный оператор:
Унарные операторы действуют только на одном операнде в выражении
int value = 6;
int negativeInt = -5;
int positiveInt = +5;
cout << (value * negativeInt); // 6 * -5 = -30
cout << (value * positiveInt); // 6 * +5 = 30
cout << (value * - negativeInt); // 6 * -(-5) = 30
cout << (value * + negativeInt); // 6 * +(-5) = -30
cout << (value * - positiveInt); // 6 * -(+5) = -30
cout << (value * + positiveInt); // 6 * +(+5) = 30
поэтому из вашего кода:
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int a = b * (c * d * + e)
//result: 2 * (3 * 4 * (+5) ) = 120
Почему он компилируется? Он компилируется, потому что +
анализируется как оператор унарного плюс, а не оператор сложения. Компилятор пытается анализировать как можно больше, не генерируя синтаксических ошибок. Итак:
d * + e
Анализируется как:
d
(операнд)*
(оператор умножения)+
(оператор унарного плюса)
e
(операнд)В то время как это:
d*++e;
Анализируется как:
d
(операнд)*
(оператор умножения)++
(оператор pre increment)
e
(операнд)Кроме того, это:
d*+++e;
Анализируется как:
d
(операнд)*
(оператор умножения)++
(оператор pre increment)
+
(оператор унарного плюса)
e
(операнд)Обратите внимание, что он не создает синтаксическую ошибку, кроме ошибки компилятора "LValue requrired".
Чтобы еще раз подправить правильные ответы, уже приведенные здесь, если вы скомпилируете флаг -s, компилятор C выведет файл сборки, в котором могут быть исследованы фактические генерируемые команды. Со следующим кодом C:
int b=1, c=2, d=3, e=4;
int a = b * (c * d * + e);
Сгенерированная сборка (с использованием gcc, компиляция для amd64) начинается с:
movl $1, -20(%ebp)
movl $2, -16(%ebp)
movl $3, -12(%ebp)
movl $4, -8(%ebp)
чтобы мы могли идентифицировать отдельные позиции памяти -20 (% ebp) как переменные b, вплоть до -8 (% ebp) как переменные e. -4 (% epp) является переменной a. Теперь вычисление выполняется как:
movl -16(%ebp), %eax
imull -12(%ebp), %eax
imull -8(%ebp), %eax
imull -20(%ebp), %eax
movl %eax, -4(%ebp)
Итак, как было прокомментировано другими людьми, отвечающими, компилятор просто рассматривает "+ e" как унарную положительную операцию. Первая команда movl помещает содержимое переменной e в регистр аккумуляторов EAX, который затем быстро умножается на содержимое переменной d или -12 (% ebp) и т.д.
Это просто базовая математика. Например:
5 * -4 = -20
5 * +4 = 5 * 4 = 20
-5 * -4 = 20
Отрицательный * Отрицательный = Положительный
Положительный * Отрицательный = Отрицательный
Позитивный * Положительный = Положительный
Это самое простое объяснение.
Минус (-) и плюс (+) просто указывают, положительно или отрицательно.
Оператор + между d и e будет рассматриваться как унарный + оператор, который будет определять только знак e. Поэтому компилятор увидит это утверждение следующим образом:
int a = b*(c*d*e) ;