Арифметические операторы
Возвращают результат конкретной арифметической операции.
| Вид оператора | Синтаксис | Перегружаемый | Образец сигнатуры (для класса T)
| |
|---|---|---|---|---|
| Функция-член класса | Свободная функция | |||
| унарный + | +a
|
Да | T T::operator+() const;
|
T operator+(const T &a);
|
| унарный - | -a
|
Да | T T::operator-() const;
|
T operator-(const T &a);
|
| сложение | a + b
|
Да | T T::operator+(const T2 &b) const;
|
T operator+(const T &a, const T2 &b);
|
| вычитание | a - b
|
Да | T T::operator-(const T2 &b) const;
|
T operator-(const T &a, const T2 &b);
|
| умножение | a * b
|
Да | T T::operator*(const T2 &b) const;
|
T operator*(const T &a, const T2 &b);
|
| деление | a / b
|
Да | T T::operator/(const T2 &b) const;
|
T operator/(const T &a, const T2 &b);
|
| взятие остатка | a % b
|
Да | T T::operator%(const T2 &b) const;
|
T operator%(const T &a, const T2 &b);
|
| побитовое НЕ | ~a
|
Да | T T::operator~() const;
|
T operator~(const T &a);
|
| побитовое И | a & b
|
Да | T T::operator&(const T2 &b) const;
|
T operator&(const T &a, const T2 &b);
|
| побитовое ИЛИ | a | b
|
Да | T T::operator|(const T2 &b) const;
|
T operator|(const T &a, const T2 &b);
|
| побитовое исключающее ИЛИ | a ^ b
|
Да | T T::operator^(const T2 &b) const;
|
T operator^(const T &a, const T2 &b);
|
| сдвиг влево | a << b
|
Да | T T::operator<<(const T2 &b) const;
|
T operator<<(const T &a, const T2 &b);
|
| сдвиг вправо | a >> b
|
Да | T T::operator>>(const T2 &b) const;
|
T operator>>(const T &a, const T2 &b);
|
| ||||
Объяснение
Все арифметические операции вычисляют результат конкретного арифметического действия и возвращают его результат. Аргументы не изменяются.
Преобразования
Если операнд, переданный арифметической операции, целочисленного типа или перечисляемого типа без области действия, то перед любым другим действием (но после преобразования l-value в r-value преобразования, если это применимо) операнд подвергается целочисленному расширению. Если операнд является массивом или функцией, то применяется преобразование массива к указателю или функции к указателю.
Для бинарных операций (кроме операций сдвига), если операнды после расширения имеют разные типы, применяются дополнительные неявные преобразования, известные как обычные арифметические преобразования с целью получения общего типа (также может быть определен через свойство типа std::common_type). Если один из операндов имеет перечисляемый тип (до применения расширения), а другой имеет тип с плавающей запятой или перечисляемый тип, отличный от типа первого операнда, данное поведение не рекомендовано. (начиная с C++20)
- Если один из операндов имеет перечисляемый тип с областью действия, никакого преобразования не выполняется: другой операнд и возвращаемое значение должны иметь тот же тип
- В противном случае, если один из операндов
long double, другой операнд преобразуется вlong double
- В противном случае, если один из операндов
double, другой операнд преобразуется вdouble
- В противном случае, если один из операндов
float, другой операнд преобразуется вfloat
- В противном случае, операнд имеет целый тип (поскольку типы bool, char, char16_t, char32_t, wchar_t и перечисляемый тип без области действия были расширены) и для получения общего типа применены целочисленные преобразования, а именно:
- Если оба операнда без знака или оба со знаком, операнд с меньшим рангом преобразования преобразуется в операнд с большим рангом преобразования целых типов
- В противном случае, если ранг преобразования операнда без знака больше или равен рангу преобразования операнда со знаком, операнд со знаком преобразуется к типу операнда без знака
- В противном случае, если тип операнда со знаком может представить все значения типа операнда без знака, операнд без знака преобразуется к типу операнда со знаком
- В противном случае оба операнда преобразуется к типу операнда без знака
Ранг преобразования растет в порядке bool, signed char, short, int, long, long long. Ранг любого типа без знака равен рангу соответствующего типа со знаком. Ранг char равен рангу signed char и unsigned char. Ранги char16_t, char32_t, и wchar_t равны рангам их подлежащих типов.
Переполнения
Арифметика для типов без знака всегда выполняется по модулю 2n
, где n-число битов в данном целом числе. Например, для unsigned int прибавление единицы к UINT_MAX даст 0, а вычитание единицы из 0 даст UINT_MAX.
Когда арифметическая операция над целым числом со знаком приводит к переполнению (значение результата не умещается в типе результата), то поведение не определено и возможны варианты:
- циклический возврат результата к нулю (wrap-around) в соответствии с правилами представления целых чисел (как правило, представимых в дополнительном коде)
- генерация исключения (зависит от платформы или опций компилятора - например,
-ftrapvв GCC и Clang)
- насыщение - ограничение результата минимальным или максимальным значением (на многих DSP-процессорах)
- операция может быть полностью удалена компилятором при оптимизации.
Окружение для операций с плавающей точкой
Если pragma-опция
#pragma STDC FENV_ACCESS поддерживается и установлена в ON, то все арифметические операции с плавающей точкой должны соблюдать текущее направление округления rounding direction и сигнализировать об ошибках в соответствии с math_errhandling, кроме вычислений в ходе статической инициализации (static initializer), когда исключения не возбуждаются и округление происходит до ближайшего представимого значения.
Свёртывание операций с плавающей точкой
Кроме случая, когда pragma-опция
#pragma STDC FP_CONTRACT поддерживается и установлена в OFF, все арифметические операции с плавающей точкой могут быть выполнены, как если бы промежуточные результаты имели бесконечные диапазон представления и точность, то есть разрешены оптимизации, пренебрегающие ошибками округления или исключениями для таких операций. Например, стандарт C++ разрешает реализацию вычисления (x*y) + z в виде одной инструкции совмещённого умножения-сложения или оптимизацию a = x*x*x*x; в виде tmp = x *x; a = tmp*tmp.
Вне зависимости от свёртывания промежуточные результаты арифметических операций с плавающей точкой могут иметь диапазон представления и точность отличные от тех, которые указаны типом (см. FLT_EVAL_METHOD).
Формально, стандарт C++ не даёт гарантий на точность операций с плавающей точкой.
Унарные арифметические операции
Выражения для унарных арифметических операций имеют форму:
+ выражение
|
(1) | ||||||||
- выражение
|
(2) | ||||||||
унарный + (целочисленное расширение).Встроенная операция унарный + возвращает значение своего операнда. Единственная ситуация, при которой данная операция не является пустой операцией, это когда операнд имеет целочисленный тип или перечисляемый тип без области действия и тип был изменен расширением, например, оно преобразовало char в int или, если операнд подлежит преобразованию l-value к r-value, массива к указателю, или функции к указателю.
Встроенная операция унарный - вычисляет величину своего операнда с противоположным знаком. Для беззнакового a, значение -a равно 2b
-a, где b - число битов после расширения.
Для каждого расширенного арифметического типа A и для каждого типа T в разрешении перегрузки в отношении данных операций, определённых пользователем принимают участие следующие сигнатуры функций:
A operator+(A) |
||
T* operator+(T*) |
||
A operator-(A) |
||
#include <iostream>
int main()
{
char c = 0x6a;
int n1 = 1;
unsigned char n2 = 1;
unsigned int n3 = 1;
std::cout << "char: " << c << " int: " << +c << '\n'
<< "-1, where 1 is signed: " << -n1 << '\n'
<< "-1, where 1 is unsigned char: " << -n2 << '\n'
<< "-1, where 1 is unsigned int: " << -n3 << '\n';
char a[3];
std::cout << "size of array: " << sizeof a << '\n'
<< "size of pointer: " << sizeof +a << '\n';
}
Вывод:
char: j int: 106
-1, where 1 is signed: -1
-1, where 1 is unsigned char: -1
-1, where 1 is unsigned int: 4294967295
size of array: 3
size of pointer: 8
Аддитивные операции
Выражения для бинарной аддитивной арифметической операции имеют форму
lhs + rhs
|
(1) | ||||||||
lhs - rhs
|
(2) | ||||||||
- оба должны иметь арифметический тип или тип перечисления без области действия. В этом случае выполняются обычные арифметические преобразования обоих операндов, определяющие тип результата
- или один из операндов должен быть указателем на полный объектный тип, а второй иметь целочисленный тип или тип перечисления без области действия. В этом случае тип результата будет иметь тип указателя
- оба должны иметь арифметический тип или тип перечисления без области действия. В этом случае выполняются обычные арифметические преобразования обоих операндов, определяющие тип результата
- lhs должен быть указателем на полный объектный тип, а rhs иметь целочисленный тип или тип перечисления без области действия. В этом случае тип результата будет иметь тип указателя
- оба должны быть указателями на один и тот же полный объектный тип, игнорируя cv-квалификаторы. В этом случае std::ptrdiff_t будет типом результата
Если операнды имеют арифметический тип или тип перечисления, то результатом бинарной операции + будет сумма обоих операндов (после обычных арифметических преобразований), а результатом бинарной операции - будет разность первого и второго операндов (после обычных арифметических преобразований), за исключением случая, когда тип поддерживает IEEE-арифметику с плавающей точкой (см. std::numeric_limits::is_iec559),
- если один из операндов
NaN, то результатом будетNaN - (
∞-∞) =NaNи возбуждается исключение FE_INVALID - (
∞+∞) =NaNи возбуждается исключение FE_INVALID
- если один из операндов
Если любой из операндов является указателем, то применяются следующие правила:
- Указатель на объект не-массив рассматривается как указатель на первый элемент некоторого массива размера 1
- Если указатель
Pуказывает наi-й элемент массива, то выраженияP+n,n+P, иP-nявляются указателями того же типа, что и указатели на(i+n)-й,(n+i)-й, и(i-n)-й элемент того же массива соответственно. Результатом прибавления к указателю целого числа может быть указатель на позицию за последним элементом (то есть такой указательP, что выражениеP-1указывает на последний элемент массива). Любые другие операции (т.е. попытки получить указатель, который не указывает на элемент того же массива или на позицию за последним элементом) ведут к неопределенному поведению - Если указатель
Pуказывает наi-й элемент массива, а указательQуказывает наj-й элемент того же массива, то выражениеP-Qпринимает значениеi-j, если это значение умещается в тип std::ptrdiff_t. Оба операнда должны указывать на элементы одного и того же массива (или на позицию за последним элементом массива), в противном случае поведение не определено. Если результат не умещается в std::ptrdiff_t, поведение не определено. - Если значение
0добавляется или вычитается из указателя, то результатом является тот же указатель. Если два указателя указывают на один и тот же объект или на позицию за последним элементом в том же массиве, или оба указателя нулевые, то результат вычитания равен(std::ptrdiff_t)0
Указатели в данных арифметических операциях удовлетворяют концепту RandomAccessIterator
Для каждой пары расширенных арифметических типов L и R и для каждого объекта типа T, в разрешении перегрузки в отношении данных операций, определённых пользователем принимают участие следующие сигнатуры функций:
LR operator+(L, R) |
||
LR operator-(L, R) |
||
T* operator+(T*, std::ptrdiff_t) |
||
T* operator+(std::ptrdiff_t, T*) |
||
T* operator-(T*, std::ptrdiff_t) |
||
std::ptrdiff_t operator-(T*, T*) |
||
,где LR является результатом обычных арифметических преобразований L и R
#include <iostream>
int main()
{
char c = 2;
unsigned int un = 2;
int n = -10;
std::cout << " 2 + (-10), where 2 is a char = " << c + n << '\n'
<< " 2 + (-10), where 2 is unsigned = " << un + n << '\n'
<< " -10 - 2.12 = " << n - 2.12 << '\n';
char a[4] = {'a', 'b', 'c', 'd'};
char* p = &a[1];
std::cout << "Pointer addition examples: " << *p << *(p + 2)
<< *(2 + p) << *(p - 1) << '\n';
char* p2 = &a[4];
std::cout << "Pointer difference: " << p2 - p << '\n';
}
Вывод:
2 + (-10), where 2 is a char = -8
2 + (-10), where 2 is unsigned = 4294967288
-10 - 2.12 = -12.12
Pointer addition examples: bdda
Pointer difference: 3
Мультипликативные операции
Выражения для бинарной мультипликативной арифметической операции имеют форму:
lhs * rhs
|
(1) | ||||||||
lhs / rhs
|
(2) | ||||||||
lhs % rhs
|
(3) | ||||||||
Бинарная операция * выполняет умножение своих операндов (после обычных арифметических преобразований), за исключением случая умножения с плавающей точкой, при котором
- результатом умножения
NaNна любое число будетNaN - результатом умножения бесконечности на
0будетNaNи возбуждается исключение FE_INVALID
Бинарная операция / делит первый операнд на второй (после обычных арифметических преобразований). Для целочисленных операндов результатом будет алгебраическое частное.
|
Частное округляется в направлении, определяемом реализацией. |
(до C++11) |
|
Частное округляется в направлении |
(начиная с C++11) |
Если делитель (второй операнд) равен 0, то поведение не определено, за исключением деления с плавающей точкой, когда тип поддерживает IEEE-арифметику с плавающей точкой (см. std::numeric_limits::is_iec559), тогда:
- если операнд равен
NaN, результатом будетNaN - результатом деления числа, не равного
0, на±0.0будет бесконечность со знаком и возбуждается исключение FE_DIVBYZERO - результатом деления
0.0на0.0будетNaNи возбуждается исключение FE_INVALID
Бинарная операция % возвращает остаток целочисленного деления первого операнда на второй (после обычных целочисленных преобразований). Оба операнда должны иметь целый тип. Если значение частного a/b представимо в типе результата, то (a/b)*b + a%b == a. Если второй операнд равен 0, то поведение не определено. Если значение частного a/b не представимо в тип результата, то поведение операций a/b и a%b не определено (результат INT_MIN%-1 не определён для чисел в дополнительном коде).
Примечание: До стандарта C++11, если один или оба операнда были отрицательными, знак остатка определялся реализацией, поскольку он зависит от направления округления при целочисленном делении. Для обеспечения переносимости следует применять функцию std::div, поведение которой определено.
Примечание: Для вычисления остатка от деления чисел с плавающей точкой см. std::remainder и std::fmod.
Для каждой пары расширенных арифметических типов LA и RA и для каждой пары расширенных целых типов LI и RI в разрешении перегрузки в отношении данных операций, определённых пользователем, принимают участие следующие сигнатуры функций:
LRA operator*(LA, RA) |
||
LRA operator/(LA, RA) |
||
LRI operator%(LI, RI) |
||
,где LRx является результатом обычных арифметических преобразований Lx и Rx
#include <iostream>
int main()
{
char c = 2;
unsigned int un = 2;
int n = -10;
std::cout << "2 * (-10), where 2 is a char = " << c * n << '\n'
<< "2 * (-10), where 2 is unsigned = " << un * n << '\n'
<< "-10 / 2.12 = " << n / 2.12 << '\n'
<< "-10 / 21 = " << n / 21 << '\n'
<< "-10 % 21 = " << n % 21 << '\n';
}Вывод:
2 * (-10), where 2 is a char = -20
2 * (-10), where 2 is unsigned = 4294967276
-10 / 2.12 = -4.71698
-10 / 21 = 0
-10 % 21 = -10Побитовые операции
Выражения для побитовых арифметических операций имеют форму:
~ rhs
|
(1) | ||||||||
lhs & rhs
|
(2) | ||||||||
lhs | rhs
|
(3) | ||||||||
lhs ^ rhs
|
(4) | ||||||||
Результатом операции ~ является побитовое НЕ (в обратном коде) значения аргумента (после расширения). Результатом операции & является значение побитового И значений операндов (после обычных арифметических преобразований). Результатом операции | является значение побитового ИЛИ значений операндов (после обычных арифметических преобразований). Результатом операции ^ является значение побитового исключающего ИЛИ (XOR) значений операндов (после обычных арифметических преобразований).
Для каждой пары расширенных целых типов L и R в разрешении перегрузки в отношении данных операций, определённых пользователем, принимают участие следующие сигнатуры функций:
R operator~(R) |
||
LR operator&(L, R) |
||
LR operator^(L, R) |
||
LR operator|(L, R) |
||
,где LR является результатом обычных арифметических преобразований L и R
#include <iostream>
int main()
{
std::cout << std::hex << std::showbase;
uint16_t mask = 0x00f0;
uint32_t a = 0x12345678;
std::cout << "Value: " << a << " mask: " << mask << '\n'
<< "Setting bits: " << (a | mask) << '\n'
<< "Clearing bits: " << (a & ~mask) << '\n'
<< "Selecting bits: " << (a & mask) << '\n';
}Вывод:
Value: 0x12345678 mask: 0xf0
Setting bits: 0x123456f8
Clearing bits: 0x12345608
Selecting bits: 0x70Операции сдвига
Выражения для операций сдвига имеют форму:
lhs << rhs
|
(1) | ||||||||
lhs >> rhs
|
(2) | ||||||||
Возвращаемым типом является тип левого операнда после целочисленных расширений.
|
Для беззнакового
Для отрицательного Для беззнакового Для отрицательного |
(до C++20) |
|
Значение Значение |
(начиная с C++20) |
В любом случае, если значение правого операнда является отрицательным или больше или равно количеству бит расширенного левого операнда, то поведение не определено.
Для каждой пары расширенных целых типов L и R в разрешении перегрузки в отношении данных операций, определённых пользователем, принимают участие следующие сигнатуры функций:
L operator<<(L, R) |
||
L operator>>(L, R) |
||
#include <iostream>
enum {ONE=1, TWO=2};
int main()
{
std::cout << std::hex << std::showbase;
char c = 0x10;
unsigned long long ull = 0x123;
std::cout << "0x123 << 1 = " << (ull << 1) << '\n'
<< "0x123 << 63 = " << (ull << 63) << '\n' // overflow in unsigned
<< "0x10 << 10 = " << (c << 10) << '\n'; // char is promoted to int
long long ll = -1000;
std::cout << std::dec << "-1000 >> 1 = " << (ll >> ONE) << '\n';
}Вывод:
0x123 << 1 = 0x246
0x123 << 63 = 0x8000000000000000
0x10 << 10 = 0x4000
-1000 >> 1 = -500Стандартная библиотека
Арифметические операции перегружены для многих типов, определённых в стандартной библиотеке.
Унарные арифметические операции
| реализует унарный + и унарный - (public функция-элемент std::chrono::duration<Rep,Period>)
| |
| применяет унарные операторы к комплексным числам (шаблон функции) | |
| применяют унарные арифметические операции к каждому элементу valarray (public функция-элемент std::valarray)
|
Аддитивные операции
(C++11) |
выполняет операции сложения и вычитания, связанные с моментом времени (шаблон функции) |
| реализует арифметические операции с duration в качестве аргументов (шаблон функции) | |
| объединяет две строки или строку и символ (шаблон функции) | |
| продвигает итератор вперед или назад (public функция-элемент std::reverse_iterator)
| |
| продвигает итератор вперед или назад (public функция-элемент std::move_iterator)
| |
| выполняет арифметику комплексных чисел над двумя комплексными значениями или комплексным и скалярным значениями (шаблон функции) | |
| применяют бинарные операторы к каждому элементу двух valarray или valarray и значению (шаблон функции) |
Мультипликативные операции
| реализует арифметические операции с duration в качестве аргументов (шаблон функции) | |
| выполняет арифметику комплексных чисел над двумя комплексными значениями или комплексным и скалярным значениями (шаблон функции) | |
| применяют бинарные операторы к каждому элементу двух valarray или valarray и значению (шаблон функции) |
Побитовые операции
| выполняет бинарное И, ИЛИ, исключающее ИЛИ и НЕ (public функция-элемент std::bitset<N>)
| |
| выполняют бинарные логические операции над наборами битов (шаблон функции) | |
применяет логическую операцию НЕ к каждому элементу std::valarray (public функция-элемент std::valarray)
| |
применяют побитовые операции И, ИЛИ, исключающее ИЛИ к элементам двух std::valarray попарно, или к элементам std::valarray и значению (шаблон функции) |
Операции сдвига
применяют операции сдвига влево/вправо к элементам двух std::valarray попарно, или к элементам std::valarray и значению (шаблон функции) | |
выполняют двоичный сдвиг влево/вправо элементов std::bitset (public функция-элемент std::bitset<N>)
|
Операции вставки / извлечения из потока
Обычно в стандартной библиотеке операции сдвига перегружены для класса потока ввода/вывода (std::ios_base& или классов, производных от него), как для типа левого операнда так и для возвращаемого типа. Такие операции известны как вставка в поток и извлечение из потока:
извлекает форматированные данные из потока ввода std::basic_istream (public функция-элемент std::basic_istream)
| |
извлекает форматированные данные из потока ввода std::basic_istream (шаблон функции) | |
вставляет форматированные данные в поток вывода std::basic_ostream (public функция-элемент std::basic_ostream)
| |
вставляет символьные данные в поток вывода std::basic_ostream (функция) | |
| сериализует и десериализует комплексное число (шаблон функции) | |
| выполняют потоковый ввод и вывод наборов битов (шаблон функции) | |
| выполняет потоковый ввод и вывод для строк (шаблон функции) | |
| выполняют потоковый вывод/ввод для объекта генератора псевдослучайных чисел (функция) | |
|
выполняют потоковый вывод/ввод для объекта распределения псевдослучайных чисел |
См. также
| Общие операторы | ||||||
|---|---|---|---|---|---|---|
| присваивание | инкремент декремент |
арифметические | логические | сравнения | доступ к элементу | другие |
|
|
|
|
|
|
|
вызов функции |
a(...)
| ||||||
| запятая | ||||||
a, b
| ||||||
| условный | ||||||
a ? b : c
| ||||||
| Специальные операторы | ||||||
|
static_cast приводит один тип к другому совместимому типу | ||||||
Документация C по Арифметические операторы
|