Spec-Zone.ru › C++

Арифметические операторы

Возвращает результат определённой арифметической операции.

Название оператора Синтаксис Примеры прототипов (для classT)
Внутри определения класса Вне определения класса
Унарный плюс +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);
Примечания
  • Все операторы в этой таблице можно перегружать.
  • Все встроенные операторы возвращают значения, и большинство перегрузок, определённых пользователем, также возвращают значения, чтобы пользовательские операторы можно было использовать так же, как встроенные. Однако в пользовательской перегрузке оператора в качестве возвращаемого типа можно использовать любой тип (включая void). В частности, перегрузки вставки и извлечения из потока для operator<< и operator>> возвращают T&.
  • T2 может иметь любой тип, включая T.

Общее описание

Все встроенные арифметические операторы вычисляют результат определённой арифметической операции и возвращают его. Аргументы не изменяются.

Преобразования

Если операнд, переданный встроенному арифметическому оператору, имеет целочисленный тип или тип перечисления без области видимости, то до выполнения любых других действий (но после преобразования lvalue в rvalue, если оно применимо) к операнду применяется целочисленное продвижение. Если операнд имеет тип массива или функции, применяются преобразования массива в указатель и функции в указатель.

Для бинарных операторов (кроме сдвигов), если типы продвинутых операндов различаются, применяются обычные арифметические преобразования.

Переполнения

Арифметические операции с беззнаковыми целыми числами всегда выполняются по модулю 2n, где n — количество бит в соответствующем целом числе. Например, для unsignedint прибавление единицы к UINT_MAX даёт 0, а вычитание единицы из 0 даёт UINT_MAX.

Если происходит переполнение при арифметической операции со знаковыми целыми числами (результат не помещается в тип результата), поведение не определено; возможные проявления такой операции включают:

  • циклический переход согласно правилам представления (обычно дополнительный код);
  • срабатывание ловушки — на некоторых платформах или из-за параметров компилятора (например, -ftrapv в GCC и Clang);
  • насыщение минимальным или максимальным значением (на многих DSP);
  • полное удаление компилятором в ходе оптимизации.

Среда вычислений с плавающей точкой

Если поддерживается #pragma STDC FENV_ACCESS и установлено значение ON, все арифметические операторы с плавающей точкой подчиняются текущему направлению округления для чисел с плавающей точкой и сообщают об арифметических ошибках с плавающей точкой согласно спецификации math_errhandling, если только они не являются частью статического инициализатора (в этом случае исключения с плавающей точкой не вызываются, а режим округления — к ближайшему).

Свёртка операций с плавающей точкой

Если #pragma STDC FP_CONTRACT не поддерживается или установлено не в значение OFF, все арифметические операции с плавающей точкой могут выполняться так, как если бы промежуточные результаты имели бесконечный диапазон и точность, то есть допускаются оптимизации, исключающие ошибки округления и исключения с плавающей точкой. Например, C++ допускает реализацию (x * y) + z одной инструкцией CPU fused multiply-add или оптимизацию a = x * x * x * x; в tmp = x * x; a = tmp * tmp.

Независимо от свёртки, диапазон и точность промежуточных результатов арифметических операций с плавающей точкой могут отличаться от указанных для их типа; см. FLT_EVAL_METHOD.

Формально стандарт C++ не даёт никаких гарантий точности операций с плавающей точкой.

Унарные арифметические операторы

Выражения с унарными арифметическими операторами имеют вид

+ expression (1)
- expression (2)
1) Унарный плюс (продвижение).
2) Унарный минус (отрицание).

Унарные операторы + и - имеют более высокий приоритет, чем все бинарные арифметические операторы, поэтому expression не может содержать бинарные арифметические операторы верхнего уровня. Эти операторы группируются справа налево:

+a - b; // equivalent to (+a) - b, NOT +(a - b)
-c + d; // equivalent to (-c) + d, NOT -(c + d)

+-e; // equivalent to +(-e), the unary + is a no-op if “e” is a built-in type
     // because any possible promotion is performed during negation already

Встроенные унарные арифметические операторы

1) Для встроенного оператора унарного плюса expression должно быть prvalue арифметического типа, типа перечисления без области видимости или типа указателя. Если expression имеет целочисленный тип или тип перечисления без области видимости, к нему применяется целочисленное продвижение. Тип результата — тип expression (возможно, продвинутый).
Результат встроенного продвижения — значение expression. Встроенная унарная операция не выполняет действий, если операнд является prvalue продвинутого целочисленного типа или типа указателя. В противном случае тип или категория значения операнда изменяется посредством целочисленного продвижения либо преобразования lvalue в rvalue, массива в указатель, функции в указатель или пользовательского преобразования. Например, в выражениях с унарным плюсом char преобразуется в int , а неконтекстное лямбда-выражение без захватов преобразуется в указатель на функцию(начиная с C++11).
2) Для встроенного оператора унарного минуса expression должно быть prvalue арифметического типа или типа перечисления без области видимости. К expression применяется целочисленное продвижение. Тип результата — тип продвинутого типа expression.
Результат встроенного отрицания — отрицательное значение продвинутого expression. Для беззнакового a значение -a равно \({\small 2^N-a}\)2N-a, где N — количество бит после продвижения.
  • Иными словами, результатом является дополнительный код операнда (операнд и результат при этом рассматриваются как беззнаковые).

Перегрузки

При разрешении перегрузки для пользовательских операторов для каждого продвинутого арифметического типа без cv-квалификаторов 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)
1) Бинарный плюс (сложение).
2) Бинарный минус (вычитание).

Бинарные операторы + и - имеют более высокий приоритет, чем все остальные бинарные арифметические операторы, кроме *, / и %. Эти операторы группируются слева направо:

a + b * c;  // equivalent to a + (b * c),  NOT (a + b) * c
d / e - f;  // equivalent to (d / e) - f,  NOT d / (e - f)
g + h >> i; // equivalent to (g + h) >> i, NOT g + (h >> i)

j - k + l - m; // equivalent to ((j - k) + l) - m

Встроенные аддитивные операторы

Для встроенных бинарных операторов сложения и вычитания оба операнда lhs и rhs должны быть prvalue, а также должно выполняться одно из следующих условий:

  • Оба операнда имеют арифметический тип или тип перечисления без области видимости. В этом случае к обоим операндам применяются обычные арифметические преобразования.
  • Точно один операнд имеет целочисленный тип или тип перечисления без области видимости. В этом случае к этому операнду применяется целочисленное продвижение.

В оставшейся части этого раздела термины «операнд(ы)», lhs и rhs относятся к преобразованным или продвинутым операндам.

1) Для встроенного сложения должно выполняться одно из следующих условий:
  • Оба операнда имеют арифметический тип. В этом случае результатом является сумма операндов.
  • Один операнд является указателем на полностью определённый тип объекта, а другой операнд имеет целочисленный тип. В этом случае целочисленное значение прибавляется к указателю (см. арифметика указателей).
2) Для встроенного вычитания должно выполняться одно из следующих условий:
  • Оба операнда имеют арифметический тип. В этом случае результатом является разность, полученная при вычитании rhs из lhs.
  • lhs является указателем на полностью определённый тип объекта, а rhs имеет целочисленный тип. В этом случае целочисленное значение вычитается из указателя (см. арифметика указателей).
  • Оба операнда являются указателями на cv-квалифицированные или неквалифицированные версии одного и того же полностью определённого типа объекта. В этом случае rhs вычитается из lhs (см. арифметика указателей).

Если оба операнда имеют тип с плавающей точкой и этот тип поддерживает арифметику с плавающей точкой IEEE (см. std::numeric_limits::is_iec559):

  • Если один из операндов — NaN, результатом будет NaN.
  • Бесконечность минус бесконечность даёт NaN, при этом вызывается FE_INVALID.
  • Бесконечность плюс отрицательная бесконечность даёт NaN, при этом вызывается FE_INVALID.

Арифметика указателей

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

  • Если P вычисляется в значение нулевого указателя, а J вычисляется в 0, результатом является значение нулевого указателя.
  • Иначе, если P указывает на элемент с номером i массива x из n элементов, а значение J обозначить как j, то P прибавляется или вычитается следующим образом:
  • Выражения P + J и J + P
    • указывают на элемент с номером i+j массива x, если i + j находится в [0, n), и
    • являются указателями за последним элементом x, если i + j равно n.
  • Выражение P - J
    • указывает на элемент с номером i-j массива x, если i - j находится в [0, n), и
    • является указателем за последним элементом x, если i - j равно n.
  • Другие значения j приводят к неопределённому поведению.
  • Иначе, если P указывает на полный объект, подобъект базового класса или подобъект-член y, а значение J обозначить как j, то P прибавляется или вычитается следующим образом:
  • Выражения P + J и J + P
    • указывают на y, если j равно 0, и
    • являются указателями за концом y, если j равно 1.
  • Выражение P - J
    • указывает на y, если j равно 0, и
    • является указателем за концом y, если j равно -1.
  • Другие значения j приводят к неопределённому поведению.
  • Иначе, если P является указателем за концом объекта z, а значение J обозначить как j:
  • Если z — массив из n элементов, P прибавляется или вычитается следующим образом:
  • Выражения P + J и J + P
    • указывают на элемент с номером n+j массива z, если n + j находится в [0, n), и
    • являются указателями за последним элементом z, если j равно 0.
  • Выражение P - J
    • указывает на элемент с номером n-j массива z, если n - j находится в [0, n), и
    • является указателем за последним элементом z, если j равно 0.
  • Другие значения j приводят к неопределённому поведению.
  • В противном случае P прибавляется или вычитается следующим образом:
  • Выражения P + J и J + P
    • указывают на z, если j равно -1, и
    • являются указателями за концом z, если j равно 0.
  • Выражение P - J
    • указывает на z, если j равно 1, и
    • является указателем за концом z, если j равно 0.
  • Другие значения j приводят к неопределённому поведению.
  • В противном случае поведение не определено.

При вычитании двух выражений-указателей P и Q тип результата — std::ptrdiff_t.

  • Если P и Q оба вычисляются в значения нулевого указателя, результатом будет 0.
  • Иначе, если P и Q указывают соответственно на элементы с номерами i и j одного и того же массива x, выражение P - Q имеет значение i − j.
    • Если i − j не представимо типом std::ptrdiff_t, поведение не определено.
  • Иначе, если P и Q указывают на один и тот же полный объект, подобъект базового класса или подобъект-член, результатом будет 0.
  • В противном случае поведение не определено.

Эти операторы арифметики указателей позволяют указателям удовлетворять требованиям LegacyRandomAccessIterator.

Для сложения и вычитания, если P или Q имеют тип «указатель на (возможно, cv-квалифицированный) T», где T и тип элемента массива не являются сходными, поведение не определено:

int arr[5] = {1, 2, 3, 4, 5};
unsigned int *p = reinterpret_cast<unsigned int*>(arr + 1);
unsigned int k = *p; // OK, the value of “k” is 2
unsigned int *q = p + 1; // undefined behavior: “p” points to int, not unsigned int

Перегрузки

При разрешении перегрузки для пользовательских операторов для каждой пары продвинутых арифметических типов 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)
1) Умножение.
2) Деление.
3) Остаток от деления.

Мультипликативные операторы имеют более высокий приоритет, чем все остальные бинарные арифметические операторы. Эти операторы группируются слева направо:

a + b * c;  // equivalent to a + (b * c),  NOT (a + b) * c
d / e - f;  // equivalent to (d / e) - f,  NOT d / (e - f)
g % h >> i; // equivalent to (g % h) >> i, NOT g % (h >> i)

j * k / l % m; // equivalent to ((j * k) / l) % m

Встроенные мультипликативные операторы

Для встроенных операторов умножения и деления оба операнда должны иметь арифметический тип или тип перечисления без области видимости. Для встроенного оператора остатка от деления оба операнда должны иметь целочисленный тип или тип перечисления без области видимости. К обоим операндам применяются обычные арифметические преобразования.

В оставшейся части этого раздела термины «операнд(ы)», lhs и rhs относятся к преобразованным операндам.

1) Результатом встроенного умножения является произведение операндов.
Если оба операнда имеют тип с плавающей точкой и этот тип поддерживает арифметику с плавающей точкой IEEE (см. std::numeric_limits::is_iec559):
  • Умножение NaN на любое число даёт NaN.
  • Умножение бесконечности на ноль даёт NaN и вызывает FE_INVALID.
2) Результатом встроенного деления является lhs, делённый на rhs. Если rhs равен нулю, поведение не определено.
Если оба операнда имеют целочисленный тип, результатом является алгебраическое частное (выполняется целочисленное деление): частное усекается в сторону нуля (дробная часть отбрасывается).
Если оба операнда имеют тип с плавающей точкой и этот тип поддерживает арифметику с плавающей точкой IEEE (см. std::numeric_limits::is_iec559):
  • Если один из операндов — NaN, результатом будет NaN.
  • Деление ненулевого числа на ±0.0 даёт бесконечность с соответствующим знаком и вызывает FE_DIVBYZERO.
  • Деление 0.0 на 0.0 даёт NaN и вызывает FE_INVALID.
3) Результатом встроенного оператора остатка является остаток от целочисленного деления lhs на rhs. Если rhs равен нулю, поведение не определено.
Если a / b представимо в типе результата, (a / b) * b + a % b == a.
Если a / b не представимо в типе результата, поведение для обоих выражений a / b и a % b не определено (это означает, что INT_MIN % -1 не определено в системах с дополнительным кодом).

Примечание: до разрешения проблемы CWG 614 (N2757), если один или оба операнда бинарного оператора % были отрицательными, знак остатка определялся реализацией, поскольку зависел от направления округления при целочисленном делении. Функция 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)
1) Побитовое НЕ.
2) Побитовое И.
3) Побитовое ИЛИ.
4) Побитовое исключающее ИЛИ.

Оператор побитового НЕ имеет более высокий приоритет, чем все бинарные арифметические операторы. Он группируется справа налево:

~a - b; // equivalent to (~a) - b, NOT ~(a - b)
~c * d; // equivalent to (~c) * d, NOT ~(c * d)

~-e; // equivalent to ~(-e)

В грамматике возникает неоднозначность, если за ~ следует имя типа или спецификатор decltype(начиная с C++11): это может быть как оператор ~, так и начало идентификатора деструктора. Неоднозначность разрешается в пользу оператора ~: ~ рассматривается как оператор ~. ~ может начинать идентификатор деструктора только в тех местах, где синтаксически невозможно сформировать оператор ~.

Все остальные побитовые логические операторы имеют более низкий приоритет, чем все остальные бинарные арифметические операторы. Побитовое И имеет более высокий приоритет, чем побитовое исключающее ИЛИ, которое, в свою очередь, имеет более высокий приоритет, чем побитовое ИЛИ. Они группируются слева направо:

a & b * c;  // equivalent to a & (b * c),  NOT (a & b) * c
d / e ^ f;  // equivalent to (d / e) ^ f,  NOT d / (e ^ f)
g << h | i; // equivalent to (g << h) | i, NOT g << (h | i)

j & k & l; // equivalent to (j & k) & l
m | n ^ o  // equivalent to m | (n ^ o)

Встроенные побитовые логические операторы

Для встроенного побитового оператора NOT rhs должен быть prvalue целочисленного типа или типа перечисления без области видимости; к rhs применяется целочисленное продвижение. Для остальных встроенных операторов побитовой логики оба операнда должны иметь целочисленный тип или тип перечисления без области видимости; к обоим операндам применяются обычные арифметические преобразования.

В оставшейся части этого раздела «операнд(ы)», lhs и rhs обозначают преобразованные или продвинутые операнд(ы).

1) Пусть операндом является x, а результатом встроенной побитовой операции NOT — r. Для каждого коэффициента x_i двоичного представления числа x соответствующий коэффициент r_i двоичного представления числа r равен 1, если x_i равен 0, и 0 в противном случае.
  • Иными словами, результат представляет собой побитовое дополнение операнда (операнд и результат рассматриваются как беззнаковые).
Тип результата r совпадает с типом операнда x.
2-4) Пусть операндами соответственно являются x и y, а результатом встроенных бинарных операций побитовой логики — r. Для каждой пары коэффициентов x_i и y_i двоичных представлений чисел x и y соответственно соответствующий коэффициент r_i двоичного представления числа r равен
2) 1, если оба x_i и y_i равны 1, и 0 в противном случае.
3) 1, если хотя бы одно из x_i и y_i равно 1, и 0 в противном случае.
4) 1, если одно (но не оба) из x_i и y_i равно 1, и 0 в противном случае.
Тип результата r совпадает с типами операндов x и y.

Перегрузки

При разрешении перегрузки для операторов, определённых пользователем, для каждой пары продвинутых целочисленных типов L и R в разрешении перегрузки участвуют следующие сигнатуры функций:

R operator~(R)
LR operator&(L, R)
LR operator^(L, R)
LR operator|(L, R)

где LR — результат применения обычных арифметических преобразований к L и R.

#include <bitset>
#include <cstdint>
#include <iomanip>
#include <iostream>

int main()
{
    std::uint16_t mask = 0x00f0;
    std::uint32_t x0 = 0x12345678;
    std::uint32_t x1 = x0 | mask;
    std::uint32_t x2 = x0 & ~mask;
    std::uint32_t x3 = x0 & mask;
    std::uint32_t x4 = x0 ^ mask;
    std::uint32_t x5 = ~x0;
    using bin16 = std::bitset<16>;
    using bin32 = std::bitset<32>;
    std::cout << std::hex << std::showbase
              << "Mask: " << mask << std::setw(49) << bin16(mask) << "\n"
                 "Value: " << x0 << std::setw(42) << bin32(x0) << "\n"
                 "Setting bits: " << x1 << std::setw(35) << bin32(x1) << "\n"
                 "Clearing bits: " << x2 << std::setw(34) << bin32(x2) << "\n"
                 "Selecting bits: " << x3 << std::setw(39) << bin32(x3) << "\n"
                 "XOR-ing bits: " << x4 << std::setw(35) << bin32(x4) << "\n"
                 "Inverting bits: " << x5 << std::setw(33) << bin32(x5) << '\n';
}

Вывод:

Mask: 0xf0                                 0000000011110000
Value: 0x12345678          00010010001101000101011001111000
Setting bits: 0x123456f8   00010010001101000101011011111000
Clearing bits: 0x12345608  00010010001101000101011000001000
Selecting bits: 0x70       00000000000000000000000001110000
XOR-ing bits: 0x12345688   00010010001101000101011010001000
Inverting bits: 0xedcba987 11101101110010111010100110000111

Операторы побитового сдвига

Выражения с операторами побитового сдвига имеют следующий вид:

lhs << rhs (1)
lhs >> rhs (2)
1) Побитовый сдвиг влево.
2) Побитовый сдвиг вправо.

Операторы побитового сдвига имеют более высокий приоритет, чем операторы побитовой логики, но более низкий приоритет, чем аддитивные и мультипликативные операторы. Эти операторы группируются слева направо:

a >> b * c;  // equivalent to a >> (b * c),  NOT (a >> b) * c
d << e & f;  // equivalent to (d << e) & f,  NOT d << (e & f)

g << h >> i; // equivalent to (g << h) >> i, NOT g << (h >> i)

Встроенные операторы побитового сдвига

Для встроенных операторов побитового сдвига оба операнда должны быть prvalue целочисленного типа или типа перечисления без области видимости. К обоим операндам применяется целочисленное продвижение.

В оставшейся части этого раздела «операнд(ы)», a, b, lhs и rhs обозначают преобразованные или продвинутые операнд(ы).

Если значение rhs отрицательно или не меньше количества битов в lhs, поведение не определено.

Для беззнакового a значение a << b равно значению a * 2b, приведённому по модулю 2N, где N — количество битов в возвращаемом типе (то есть выполняется побитовый сдвиг влево, а биты, сдвинутые за пределы целевого типа, отбрасываются).

Для знакового и неотрицательного a, если a * 2b представимо в беззнаковой версии возвращаемого типа, это значение, преобразованное к знаковому типу, является значением a << b (это позволяет создавать INT_MIN как 1 << 31); в противном случае поведение не определено.

Для отрицательного a поведение a << b не определено.

Для беззнакового a, а также для знакового и неотрицательного a значение a >> b равно целой части a/2b.

Для отрицательного a значение a >> b определяется реализацией (в большинстве реализаций выполняется арифметический сдвиг вправо, поэтому результат остаётся отрицательным).

(до C++20)

Значение a << b — это единственное значение, сравнимое с a * 2b по модулю 2N, где N — количество битов в возвращаемом типе (то есть выполняется побитовый сдвиг влево, а биты, сдвинутые за пределы целевого типа, отбрасываются).

Значение a >> b равно a/2b с округлением в сторону отрицательной бесконечности (иными словами, сдвиг вправо для знакового a является арифметическим сдвигом вправо).

(начиная с C++20)

Тип результата совпадает с типом lhs.

Перегрузки

При разрешении перегрузки для операторов, определённых пользователем, для каждой пары продвинутых целочисленных типов 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

Стандартная библиотека

Арифметические операторы перегружены для многих типов стандартной библиотеки.

Унарные арифметические операторы

operator+operator-
реализует унарные операторы + и -
(открытая функция-член std::chrono::duration<Rep,Period>)
operator+operator-
применяет унарные операторы к комплексным числам
(шаблон функции)
operator+operator-operator~operator!
применяет унарный арифметический оператор к каждому элементу valarray
(открытая функция-член std::valarray<T>)

Аддитивные операторы

operator+operator-
(C++11)
выполняет сложение и вычитание с участием момента времени
(шаблон функции)
operator+operator-operator*operator/operator%
(C++11)
реализует арифметические операции с длительностями в качестве аргументов
(шаблон функции)
operator+operator-
(C++20)
прибавляет или вычитает year_month_day и некоторое количество лет или месяцев
(функция)
operator+
объединяет две строки, строку и символ или строку и string_view
(шаблон функции)
operator+operator-
перемещает итератор вперёд или назад
(открытая функция-член std::reverse_iterator<Iter>)
operator+operator-
перемещает итератор вперёд или назад
(открытая функция-член std::move_iterator<Iter>)
operator+operator-operator*operator/
выполняет арифметические операции с комплексными числами для двух комплексных значений либо комплексного числа и скаляра
(шаблон функции)
operator+operator-operator*operator/operator%operator&operator|operator^operator<<operator>>operator&&operator
применяет бинарные операторы к каждому элементу двух valarray либо valarray и значения
(шаблон функции)

Мультипликативные операторы

operator+operator-operator*operator/operator%
(C++11)
реализует арифметические операции с длительностями в качестве аргументов
(шаблон функции)
operator+operator-operator*operator/
выполняет арифметические операции с комплексными числами для двух комплексных значений либо комплексного числа и скаляра
(шаблон функции)
operator+operator-operator*operator/operator%operator&operator|operator^operator<<operator>>operator&&operator
применяет бинарные операторы к каждому элементу двух valarray либо valarray и значения
(шаблон функции)

Операторы побитовой логики

operator&=operator|=operator^=operator~
выполняет побитовые AND, OR, XOR и NOT
(открытая функция-член std::bitset<N>)
operator&operator|operator^
выполняет бинарные логические операции над битовыми наборами
(шаблон функции)
operator~
применяет унарный арифметический оператор к каждому элементу valarray
(открытая функция-член std::valarray<T>)
operator^operator&operator|
применяет бинарные операторы к каждому элементу двух valarray либо valarray и значения
(шаблон функции)

Операторы побитового сдвига

operator<<operator>>
применяет бинарные операторы к каждому элементу двух valarray либо valarray и значения
(шаблон функции)
operator<<operator>>
выполняет бинарный сдвиг влево и сдвиг вправо
(открытая функция-член std::bitset<N>)

Операторы вставки в поток и извлечения из потока

В стандартной библиотеке операторы побитового сдвига часто перегружаются так, чтобы в качестве левого операнда и возвращаемого типа использовался поток ввода-вывода (std::ios_base& или один из производных от него классов). Такие операторы называются операторами вставки в поток и операторами извлечения из потока:

operator>>
извлекает форматированные данные
(открытая функция-член std::basic_istream<CharT,Traits>)
operator>>(std::basic_istream)
извлекает символы и массивы символов
(шаблон функции)
operator<<
вставляет форматированные данные
(открытая функция-член std::basic_ostream<CharT,Traits>)
operator<<(std::basic_ostream)
вставляет символьные данные или выполняет вставку в поток rvalue
(шаблон функции)
operator<<operator>>
сериализует и десериализует комплексное число
(шаблон функции)
operator<<operator>>
выполняет ввод и вывод битовых наборов с помощью потоков
(шаблон функции)
operator<<operator>>
выполняет ввод и вывод строк с помощью потоков
(шаблон функции)
operator<<operator>>
(C++11)
выполняет ввод и вывод псевдослучайного генератора чисел с помощью потоков
(шаблон функции)
operator<<operator>>
(C++11)
выполняет ввод и вывод распределения псевдослучайных чисел с помощью потоков
(шаблон функции)

Сведения об исправлениях

Следующие отчёты об исправлениях, меняющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применён к Опубликованное поведение Правильное поведение
CWG 614 C++98 алгебраическое частное от деления целых чисел округлялось
в направлении, определяемом реализацией
алгебраическое частное от деления
целых чисел усекается к нулю
(дробная часть отбрасывается)
CWG 1450 C++98 результат a / b не был определён, если
он не представим в типе результата
в этом случае поведение как a / b, так и
a % b не определено
CWG 1457 C++98 поведение при сдвиге крайнего левого 1 бита
положительного знакового значения в знаковый бит не было определено
поведение стало определённым
CWG 1504 C++98 указатель на базовый подобъект элемента массива
можно было использовать в арифметике указателей
в этом случае поведение
не определено
CWG 1515 C++98 законам арифметики по модулю 2n должны были подчиняться только беззнаковые целые числа, объявленные беззнаковыми применяется ко всем беззнаковым целым числам
CWG 1642 C++98 арифметические операторы допускают операнды-значения lvalue некоторые операнды должны быть значениями rvalue
CWG 1865 C++98 решение вопроса CWG 1504 сделало поведение
арифметики указателей с указателями на элементы массива
неопределённым, если тип указателя и тип элемента массива
имеют разные cv-квалификаторы не на верхнем уровне
поведение стало определённым
CWG 1971 C++98 было неясно, применяется ли правило устранения
неоднозначности ~ к таким случаям, как ~X(0)
правило применяется и к таким случаям
CWG 2419 C++98 указатель на объект, не являющийся массивом, считался указателем на первый элемент массива размера 1
в арифметике указателей только в том случае, если указатель получен с помощью &
применяется ко всем указателям
на объекты, не являющиеся массивами
CWG 2626 C++98 результат встроенного operator~ просто
назывался «побитовым дополнением» без точного определения
результат описан через
двоичное представление
CWG 2724 C++20 направление округления при арифметическом сдвиге вправо было неясно разъяснено
CWG 2853 C++98 к указателю за концом объекта нельзя было
прибавлять целое число или вычитать его
это разрешено

См. также

Приоритет операторов

Перегрузка операторов

Распространённые операторы
Присваивание Инкремент,
декремент
Арифметические Логические Сравнения Доступ к
членам
Прочие
a = b
a += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b
++a
--a
a++
a--
+a
-a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b
!a
a && b
a || b
a == b
a != b
a < b
a > b
a <= b
a >= b
a <=> b
a[...]
*a
&a
a->b
a.b
a->*b
a.*b
вызов функции

a(...)
запятая

a, b
условный оператор

a ? b : c
Специальные операторы

static_cast преобразует тип в другой связанный тип
dynamic_cast выполняет преобразование в иерархиях наследования
const_cast добавляет или удаляет cv-квалификаторы
reinterpret_cast преобразует тип в несвязанный тип
Приведение в стиле C преобразует один тип в другой с помощью сочетания static_cast, const_cast и reinterpret_cast
new создаёт объекты с динамической длительностью хранения
delete уничтожает объекты, ранее созданные выражением new, и освобождает занятую ими область памяти
sizeof запрашивает размер типа
sizeof... запрашивает размер пакета (начиная с C++11)
typeid запрашивает информацию о типе
noexcept проверяет, может ли выражение выбросить исключение (начиная с C++11)
alignof запрашивает требования к выравниванию типа (начиная с C++11)
^^ создаёт значение отражения на основе грамматической конструкции (начиная с C++26)

Документация по C для Арифметические операторы

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/operator_arithmetic

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API