Spec-Zone.ru › C++

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

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

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

Общее объяснение

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

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

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

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

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

Арифметика целых без знака всегда выполняется по модулю 2n, где n — количество бит в этом целом числе. Например, для unsigned int добавление единицы к 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 с одной инструкцией Fused Multiply-Add процессора или оптимизацию a = x * x * x * x; как tmp = x * x; a = tmp * tmp.

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

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

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

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

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

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

+a - b; // equivalent to (+a) - b, NOT +(a - b)
-c + d; // equivalent to (-c) + d, NOT -(a + b)
 
+-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

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

1) Для встроенного унарного оператора плюс выражение должно быть значением-выражением арифметического, перечислимого или указательного типа. Целочисленное продвижение выполняется для выражения, если оно имеет целочисленный или перечислимый тип. Тип результата — это (возможно, продвинутый) тип выражения.
Результат встроенного продвижения — это значение выражения. Встроенная унарная операция является пустой операцией, если операнд является значением-выражением продвинутого целочисленного типа или указателя. В противном случае тип или категория значения операнда изменяются с помощью целочисленного продвижения или преобразований «lvalue в rvalue», «массив в указатель», «функция в указатель» или пользовательских преобразований. Например, char преобразуется в int , а безгенерическая беззахватная лямбда-выражение преобразуется в указатель на функцию(с C++11) в выражениях унарного плюса.
2) Для встроенного унарного оператора минус выражение должно быть значением-выражением арифметического или перечислимого типа. Целочисленное продвижение выполняется для выражения. Тип результата — это тип продвинутого типа выражения.
Результат встроенного отрицания — это отрицание продвинутого выражения. Для беззнаковых 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-квалифицированные или 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 и 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

Битовые логические операторы

Выражения битовых логических операторов имеют вид

lhs ~ 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)

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

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

В остальном описании в этом разделе, «операнды», lhs и rhs относятся к преобразованным или продвинутым операндам.

1) Учитывая операнд как x и результат встроенной операции битового НЕ как 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)

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

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

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

END_OF_DOCUMENT_MARKER

Для беззнакового 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. Если значение rhs отрицательное или не меньше количества битов в 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+
конкатенирует две строки или строку и char
(функция-шаблон)
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^
выполняет бинарные логические операции над bitset
(функция-шаблон)
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)
вставляет символьные данные или вставляет в поток правого значения
(шаблон функции)
operator<<operator>>
сериализует и десериализует комплексное число
(шаблон функции)
operator<<operator>>
выполняет ввод и вывод bitset в поток
(шаблон функции)
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 только целые беззнаковые, которые объявлены unsigned
должны подчиняться законам арифметики по модулю 2n
применимо ко всем беззнаковым целым числам
CWG 1642 C++98 арифметические операторы позволяют своим операндам быть lvalue некоторые операнды должны быть rvalue
CWG 1865 C++98 разрешение CWG issue 1504 сделало поведение
арифметики указателей, включающей указатели на элементы массива
неопределенным, если тип указываемого объекта и тип элемента
массива имеют различные cv-квалификаторы на уровнях, не являющихся верхними
сделано определенным
CWG 1971 C++98 не было ясно, применяется ли правило разрешения
неопределенности ~ к таким случаям, как ~X(0)
правило применяется к таким случаям
CWG 2419 C++98 указатель на объект, не являющийся массивом, рассматривался как
указатель на первый элемент массива размером 1 в арифметике указателей,
если указатель получен посредством &
применимо ко всем указателям
на объекты, не являющиеся массивами
CWG 2626 C++98 результат встроенного operator~ был просто
'дополнительным кодом' без надлежащего определения
результат сформулирован в терминах
представления в системе счисления по основанию 2
CWG 2724 C++20 направление округления арифметического сдвига вправо было неясным сделано ясным

См. также

Порядок приоритета операторов

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

END_OF_DOCUMENT_MARKER
Общие операторы
присваивание инкремент
декремент
арифметические логические сравнения доступ к
членам
другие

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 документация для Арифметических операторов

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

Spec-Zone.ru

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