Namespaces
Variants

Arithmetic operators

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

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

Содержание

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

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

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

Если операнд, переданный встроенному арифметическому оператору, имеет целочисленный тип или тип неограниченного перечисления, то до выполнения любых других действий (но после преобразования lvalue-to-rvalue, если применимо) операнд подвергается integral promotion . Если операнд имеет тип массива или функции, применяются преобразования array-to-pointer и function-to-pointer .

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

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

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

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

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

Окружение с плавающей точкой

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

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

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

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

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

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

Унарные арифметические операторы имеют следующий вид

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

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

+a - b; // эквивалентно (+a) - b, НЕ +(a - b)
-c + d; // эквивалентно (-c) + d, НЕ -(c + d)
+-e; // эквивалентно +(-e), унарный + является пустой операцией, если "e" — встроенный тип
     // поскольку все возможные преобразования выполняются уже во время отрицания

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

1) Для встроенного оператора унарного плюса, expression должно быть prvalue арифметического типа, неограниченного перечисления или указателя. Целочисленное продвижение выполняется для expression если оно имеет целочисленный тип или тип неограниченного перечисления. Тип результата является (возможно продвинутым) типом expression .
Результатом встроенного повышения является значение expression . Встроенная унарная операция является no-op, если операнд является prvalue продвинутого целочисленного типа или типа указателя. В противном случае тип или категория значения операнда изменяется посредством целочисленного повышения, преобразования lvalue-to-rvalue, array-to-pointer, function-to-pointer или пользовательского преобразования. Например, char преобразуется в int , а необобщенное лямбда-выражение без захвата lambda expression преобразуется в указатель на функцию (since C++11) в выражениях унарного плюса.
2) Для встроенного унарного оператора минус, expression должно быть prvalue арифметического типа или типа неограниченного перечисления. Над expression выполняется целочисленное повышение. Тип результата соответствует типу повышенного типа expression .
Результатом встроенного отрицания является отрицание продвинутого выражения . Для беззнакового a , значение - a равно 2 N
-a
, где N — количество битов после продвижения.
  • Другими словами, результат является дополнительным кодом операнда (где операнд и результат рассматриваются как беззнаковые).

Перегрузки

В разрешении перегрузки для пользовательских операторов , для каждого непромотированного арифметического типа без cv-квалификаторов A и для каждого типа T , следующие сигнатуры функций участвуют в разрешении перегрузки:

A operator + ( A )
T * operator + ( T * )
A operator - ( A )
**Примечание:** Весь код внутри тегов ` ` сохранен без изменений, так как содержит C++ специфичные термины и синтаксис, которые не подлежат переводу согласно требованиям. HTML структура и форматирование полностью сохранены.
#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)
**Примечание:** В данном случае весь текст на странице состоит из C++ специфических терминов (lhs, rhs) и HTML-разметки, которые не подлежат переводу согласно инструкциям. Числовые обозначения (1) и (2) также сохраняются в оригинальном виде.
1) Бинарный плюс (сложение).
2) Бинарный минус (вычитание).

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

a + b * c;  // эквивалентно a + (b * c),  НЕ (a + b) * c
d / e - f;  // эквивалентно (d / e) - f,  НЕ d / (e - f)
g + h >> i; // эквивалентно (g + h) >> i, НЕ g + (h >> i)
j - k + l - m; // эквивалентно ((j - k) + l) - m

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

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

  • Оба операнда имеют арифметический тип или тип неограниченного перечисления. В этом случае usual arithmetic conversions применяются к обоим операндам.
  • Ровно один операнд имеет целочисленный тип или тип неограниченного перечисления. В этом случае к этому операнду применяется integral promotion.

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

1) Для встроенного сложения должно быть выполнено одно из следующих условий:
  • Оба операнда имеют арифметический тип. В этом случае результатом является сумма операндов.
  • Один операнд является указателем на полностью определенный объектный тип, а другой операнд имеет целочисленный тип. В этом случае целочисленное значение добавляется к указателю (см. pointer arithmetic ).
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, значение "k" равно 2
unsigned int *q = p + 1; // неопределенное поведение: "p" указывает на int, а не на unsigned int

Перегрузки

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

LR оператор + ( L, R )
LR оператор - ( L, R )
T * оператор + ( T * , std:: ptrdiff_t )
T * оператор + ( std:: ptrdiff_t , T * )
T * оператор - ( T * , std:: ptrdiff_t )
std:: ptrdiff_t оператор - ( 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;  // эквивалентно a + (b * c),  НЕ (a + b) * c
d / e - f;  // эквивалентно (d / e) - f,  НЕ d / (e - f)
g % h >> i; // эквивалентно (g % h) >> i, НЕ g % (h >> i)
j * k / l % m; // эквивалентно ((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 issue 614 была решена ( N2757 ), если один или оба операнда бинарного оператора % были отрицательными, знак остатка определялся реализацией, поскольку он зависит от направления округления при целочисленном делении. Функция std::div обеспечивала хорошо определённое поведение в этом случае.

Примечание: для вещественного остатка от деления смотрите std::remainder и std::fmod .

Перегрузки

В разрешении перегрузки для пользовательских операторов , для каждой пары продвинутых арифметических типов LA и RA и для каждой пары продвинутых целочисленных типов LI и RI следующие сигнатуры функций участвуют в разрешении перегрузки:

LRA operator * ( LA, RA )
LRA operator / ( LA, RA )
LRI operator % ( LI, RI )
**Примечание:** Весь текст в данном HTML-фрагменте находится внутри тегов ` `, которые содержат C++ код. Согласно вашим инструкциям, текст внутри таких тегов (которые являются разновидностью тегов для кода) не должен переводиться, чтобы сохранить техническую точность C++ специфичных терминов и синтаксиса.

где 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)
**Примечание:** В данном случае весь текст на странице состоит из C++ специфических терминов (rhs, lhs) и HTML тегов, которые не подлежат переводу согласно инструкциям. Числовые обозначения (1), (2), (3), (4) также сохраняются в оригинальном виде.
1) Побитовое НЕ.
2) Побитовое И.
3) Побитовое ИЛИ.
4) Побитовое исключающее ИЛИ.

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

~a - b; // эквивалентно (~a) - b, НЕ ~(a - b)
~c * d; // эквивалентно (~c) * d, НЕ ~(c * d)
~-e; // эквивалентно ~(-e)

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

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

a & b * c;  // эквивалентно a & (b * c),  НЕ (a & b) * c
d / e ^ f;  // эквивалентно (d / e) ^ f,  НЕ d / (e ^ f)
g << h | i; // эквивалентно (g << h) | i, НЕ g << (h | i)
j & k & l; // эквивалентно (j & k) & l
m | n ^ o  // эквивалентно m | (n ^ o)

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

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

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

1) Дано операнд x и результат встроенной побитовой операции НЕ как r . Для каждого коэффициента x_i в представлении числа x по основанию 2, соответствующий коэффициент r_i в представлении числа r по основанию 2 равен 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 )
**Примечание:** Весь текст в данном HTML-фрагменте находится внутри тегов ` `, которые содержат исключительно C++ код. Согласно вашим инструкциям, код внутри таких тегов не подлежит переводу. Поэтому перевод не требуется - весь контент сохранен в оригинальном виде.

где 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 << std::setw(49) << bin16(mask) << "\n"
                 "Значение: " << x0 << std::setw(42) << bin32(x0) << "\n"
                 "Установка битов: " << x1 << std::setw(35) << bin32(x1) << "\n"
                 "Сброс битов: " << x2 << std::setw(34) << bin32(x2) << "\n"
                 "Выбор битов: " << x3 << std::setw(39) << bin32(x3) << "\n"
                 "Исключающее ИЛИ: " << x4 << std::setw(35) << bin32(x4) << "\n"
                 "Инверсия битов: " << x5 << std::setw(33) << bin32(x5) << '\n';
}

Вывод:

Маска: 0xf0                                 0000000011110000
Значение: 0x12345678          00010010001101000101011001111000
Установка битов: 0x123456f8   00010010001101000101011011111000
Сброс битов: 0x12345608  00010010001101000101011000001000
Выбор битов: 0x70       00000000000000000000000001110000
Исключающее ИЛИ: 0x12345688   00010010001101000101011010001000
Инверсия битов: 0xedcba987 11101101110010111010100110000111

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

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

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

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

a >> b * c;  // эквивалентно a >> (b * c),  НЕ (a >> b) * c
d << e & f;  // эквивалентно (d << e) & f,  НЕ d << (e & f)
g << h >> i; // эквивалентно (g << h) >> i, НЕ g << (h >> i)

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

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

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

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

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

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

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

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

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

(до C++20)

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

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

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

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

Перегрузки

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

L operator << ( L, R )
L operator >> ( L, R )
**Примечание:** В данном случае весь текст внутри тегов ` ` является C++ кодом, поэтому согласно инструкциям он не подлежит переводу. HTML структура и форматирование полностью сохранены.
#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" // переполнение в беззнаковом
                 "0x10 << 10 = " << (c << 10) << '\n';   // char продвигается до 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

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

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

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

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

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

выполняет операции сложения и вычитания с участием точки времени
(шаблон функции)
реализует арифметические операции с длительностями в качестве аргументов
(шаблон функции)
добавляет или вычитает year_month_day и некоторое количество лет или месяцев
(функция)
объединяет две строки, строку и char , или строку и string_view
(шаблон функции)
перемещает вперед или назад итератор
(публичная функция-член std::reverse_iterator<Iter> )
перемещает вперед или назад итератор
(публичная функция-член std::move_iterator<Iter> )
выполняет арифметические операции над комплексными числами для двух комплексных значений или комплексного числа и скаляра
(шаблон функции)
применяет бинарные операторы к каждому элементу двух valarray или valarray и значения
(шаблон функции)

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

реализует арифметические операции с длительностями в качестве аргументов
(шаблон функции)
выполняет арифметические операции над двумя комплексными числами или комплексным числом и скаляром
(шаблон функции)
применяет бинарные операторы к каждому элементу двух valarray или valarray и значения
(шаблон функции)

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

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

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

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

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

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

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

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам 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
должны подчиняться законам арифметики по модулю 2 n
применяется ко всем беззнаковым целым
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~ был просто
'дополнением до единицы' без надлежащего определения
результат формулируется в терминах
двоичного представления
CWG 2724 C++20 направление округления арифметического сдвига вправо было неясным прояснено
CWG 2853 C++98 указатель за концом объекта не мог
складываться или вычитаться с целым числом
может

Смотрите также

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

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

Общие операторы
assignment increment
decrement
arithmetic logical comparison member
access
other

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-style приведение преобразует один тип в другой с помощью комбинации static_cast , const_cast и reinterpret_cast
new создает объекты с динамической продолжительностью хранения
delete уничтожает объекты, ранее созданные выражением new, и освобождает полученную область памяти
sizeof запрашивает размер типа
sizeof... запрашивает размер пакета (since C++11)
typeid запрашивает информацию о типе
noexcept проверяет, может ли выражение генерировать исключение (since C++11)
alignof запрашивает требования к выравниванию типа (since C++11)

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