Arithmetic operators
특정 산술 연산의 결과를 반환합니다.
| 연산자 이름 | 구문 | 프로토타입 예시 ( 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 ) ; | |
| 비트 NOT | ~a | T T :: operator ~ ( ) const ; | T operator~ ( const T & a ) ; | |
| 비트 AND | a & b | T T :: operator & ( const T2 & b ) const ; | T operator & ( const T & a, const T2 & b ) ; | |
| 비트 OR | a | b | T T :: operator | ( const T2 & b ) const ; | T operator | ( const T & a, const T2 & b ) ; | |
| 비트별 XOR | 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 ) ; | |
|
||||
목차 |
일반적인 설명
모든 내장 산술 연산자는 특정 산술 연산의 결과를 계산하고 그 결과를 반환합니다. 인수는 수정되지 않습니다.
변환
내장 산술 연산자에 전달된 피연산자가 정수형 또는 비범위 열거형인 경우, 다른 모든 동작 전에(그러나 적용 가능한 경우 lvalue-to-rvalue 변환 이후) 피연산자는 integral promotion 을 거칩니다. 피연산자가 배열 또는 함수 타입을 가지는 경우, array-to-pointer 및 function-to-pointer 변환이 적용됩니다.
이항 연산자(시프트 연산 제외)의 경우, 승격된 피연산자들의 타입이 서로 다르면 일반 산술 변환 이 적용됩니다.
오버플로우
부호 없는 정수 연산은 항상
modulo 2
n
으로 수행되며, 여기서 n은 해당 정수의 비트 수입니다. 예를 들어
unsigned
int
의 경우,
UINT_MAX
에 1을 더하면
0
이 되고,
0
에서 1을 빼면
UINT_MAX
가 됩니다.
부호 있는 정수 산술 연산이 오버플로될 때(결과가 결과 타입에 맞지 않음), 그 동작은 정의되지 않습니다 — 이러한 연산의 가능한 현상으로는 다음이 포함됩니다:
- 이는 표현 방식의 규칙에 따라 감싸기(wrap around)됩니다 (일반적으로 two's complement ).
-
트랩(trap)됩니다 — 일부 플랫폼이나 컴파일러 옵션에 의해 (예: GCC와 Clang의
-ftrapv). - 최소값 또는 최대값으로 포화(saturate)됩니다 (많은 DSP에서).
- 컴파일러에 의해 완전히 optimized out by the compiler 됩니다.
부동소수점 환경
만약
#pragma STDC FENV_ACCESS
가 지원되고
ON
으로 설정된 경우, 모든 부동 소수점 연산자는 현재 부동 소수점
반올림 방향
을 따르며,
math_errhandling
에 지정된 대로 부동 소수점 산술 오류를 보고합니다. 단,
정적 초기화
의 일부인 경우는 예외입니다(이 경우 부동 소수점 예외가 발생하지 않으며 반올림 모드는 가장 가까운 값으로 설정됩니다).
부동 소수점 축약
#pragma STDC FP_CONTRACT
가 지원되고
OFF
로 설정되지 않는 한, 모든 부동 소수점 연산은 중간 결과가 무한한 범위와 정밀도를 가진 것처럼 수행될 수 있습니다. 즉, 반올림 오류와 부동 소수점 예외를 생략하는 최적화가 허용됩니다. 예를 들어, C++은
(
x
*
y
)
+
z
를 단일 FMA(Fused Multiply-Add) CPU 명령어로 구현하거나
a
=
x
*
x
*
x
*
x
;
를
tmp
=
x
*
x
;
a
=
tmp
*
tmp
로 최적화하는 것을 허용합니다.
계약과 무관하게, 부동 소수점 연산의 중간 결과는 해당 타입이 나타내는 범위와 정밀도와 다를 수 있습니다. 자세한 내용은 FLT_EVAL_METHOD 를 참조하십시오.
공식적으로, C++ 표준은 부동 소수점 연산의 정확성에 대해 어떠한 보장도 하지 않습니다.
단항 산술 연산자
단항 산술 연산자 표현식의 형식은 다음과 같습니다.
+
표현식
|
(1) | ||||||||
-
표현식
|
(2) | ||||||||
단항
+
및
-
연산자는 모든 이항 산술 연산자보다 높은
우선순위
를 가지므로,
표현식
에는 최상위 수준의 이항 산술 연산자를 포함할 수 없습니다. 이러한 연산자는 오른쪽에서 왼쪽으로 결합합니다:
+a - b; // (+a) - b와 동일, +(a - b)가 아님 -c + d; // (-c) + d와 동일, -(c + d)가 아님 +-e; // +(-e)와 동일, "e"가 built-in type인 경우 단항 +는 no-op임 // 부정 연산 수행 중에 가능한 모든 promotion이 이미 이루어지기 때문
내장 단항 산술 연산자
-a 입니다. 여기서 N 은 승격 후의 비트 수입니다.
- 다른 말로, 결과는 피연산자의 2의 보수입니다(피연산자와 결과는 부호 없는 것으로 간주).
오버로드
사용자 정의 연산자에 대한 오버로드 해결
에서, 모든 cv-unqualified 승격된 산술 타입
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) | ||||||||
이항
+
및
-
연산자는
*
,
/
,
%
를 제외한 다른 모든 이항 산술 연산자보다 높은
우선순위
를 가집니다. 이러한 연산자들은 왼쪽에서 오른쪽으로 결합합니다:
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 모두 prvalue여야 하며, 다음 조건 중 하나를 만족해야 합니다:
- 두 피연산자 모두 산술 타입이나 비한정 열거형 타입을 가집니다. 이 경우, usual arithmetic conversions 가 두 피연산자에 대해 수행됩니다.
- 정확히 하나의 피연산자만 정수형이나 비한정 열거형 타입을 가집니다. 이 경우, 해당 피연산자에 integral promotion이 적용됩니다.
이 섹션의 나머지 설명에서 "피연산자(들)", lhs 와 rhs 는 변환되거나 승격된 피연산자(들)를 가리킵니다.
- 두 피연산자가 모두 산술 타입을 가져야 합니다. 이 경우 결과는 피연산자들의 합입니다.
- 한 피연산자가 완전히 정의된 객체 타입에 대한 포인터이고, 다른 피연산자가 정수 타입을 가져야 합니다. 이 경우 정수 값이 포인터에 더해집니다 (참조: pointer arithmetic ).
- 두 피연산자가 모두 산술 타입을 가져야 합니다. 이 경우 결과는 lhs 에서 rhs 를 뺀 차이입니다.
- lhs 가 완전히 정의된 객체 타입에 대한 포인터이고, rhs 가 정수 타입을 가져야 합니다. 이 경우 정수 값이 포인터에서 감산됩니다( pointer arithmetic 참조).
- 두 피연산자가 모두 동일한 완전히 정의된 객체 타입의 cv-qualified 또는 cv-unqualified 버전에 대한 포인터여야 합니다. 이 경우 rhs 가 lhs 에서 감산됩니다( pointer arithmetic 참조).
두 피연산자가 모두 부동 소수점 타입이고, 해당 타입이 IEEE 부동 소수점 연산을 지원하는 경우(참조: std::numeric_limits::is_iec559 ):
- 피연산자 중 하나가 NaN이면 결과는 NaN입니다.
- 무한대에서 무한대를 빼면 NaN이 되며, FE_INVALID 가 발생합니다.
- 무한대와 음의 무한대를 더하면 NaN이 되며, FE_INVALID 가 발생합니다.
포인터 연산
정수형을 가진 표현식 J 가 포인터 타입의 표현식 P 에 더해지거나 빼질 때, 결과는 P 의 타입을 가집니다.
- 만약 P 가 null 포인터 값 으로 평가되고 J 가 0 으로 평가되면, 결과는 null 포인터 값입니다.
-
그렇지 않고
P
가
n
개의 요소를 가진 배열 객체
x
의
i번째 요소를 가리키고, J 의 값이 j 로 주어지면, P 는 다음과 같이 더하거나 뺍니다:
-
- 표현식 P + J 과 J + P
-
-
i+j번째 요소를 가리키며, 이는 i + j 가[ 0 ,n)범위 내에 있을 경우에 해당합니다. 그리고 - i + j 가 n 인 경우, 마지막 요소의 끝을 지나는 포인터입니다.
-
- 표현식 P - J
-
-
i-j번째 요소를 가리키며, 이는 i - j 가[ 0 ,n)범위 내에 있을 경우에 해당합니다. 그리고 - i - j 가 n 인 경우, 마지막 요소의 끝을 지나는 포인터입니다.
-
- 다른 j 값들은 정의되지 않은 행동을 초래합니다.
- 그렇지 않고, P 가 완전한 객체, 기반 클래스 하위 객체 또는 멤버 하위 객체 y 를 가리키는 경우, J 의 값이 j 일 때, P 는 다음과 같이 더하거나 뺍니다:
-
- 표현식 P + J 과 J + P
-
- j 가 0 이면 y 를 가리키고,
- j 가 1 이면 y 의 끝을 지난 포인터입니다.
- 표현식 P - J
-
- j 가 0 이면 y 를 가리키고,
- j 가 - 1 이면 y 의 끝을 지난 포인터입니다.
- 다른 j 값들은 정의되지 않은 동작을 초래합니다.
- 그렇지 않고, P 가 객체 z 의 끝을 지난 포인터이고, J 의 값이 j 인 경우:
-
- 만약 z 가 n 개의 요소를 가진 배열 객체라면, P 는 다음과 같이 더하거나 뺍니다:
-
- 표현식 P + J 과 J + P
-
-
n
+
j
가
[ 0 ,n)범위 내에 있을 경우 z 의n+j번째 요소를 가리키고, - j 가 0 일 경우 z 의 마지막 요소 바로 다음을 가리키는 포인터입니다.
-
n
+
j
가
- 표현식 P - J
-
-
n
-
j
가
[ 0 ,n)범위 내에 있을 경우 z 의n-j번째 요소를 가리키고, - j 가 0 일 경우 z 의 마지막 요소 바로 다음을 가리키는 포인터입니다.
-
n
-
j
가
- 다른 j 값들은 정의되지 않은 행동을 초래합니다.
- 그렇지 않으면, P 는 다음과 같이 더하거나 뺍니다:
-
- 표현식 P + J 과 J + P
-
- j 가 - 1 일 경우 z 를 가리키고,
- j 가 0 일 경우 z 의 끝 바로 다음을 가리키는 포인터입니다.
- 표현식 P - J
-
- j 가 1 일 경우 z 를 가리키고,
- j 가 0 일 경우 z 의 끝 바로 다음을 가리키는 포인터입니다.
- 다른 j 값들은 정의되지 않은 행동을 초래합니다.
- 그 외의 경우, 동작은 정의되지 않습니다.
두 포인터 표현식 P 와 Q 를 뺄 때, 결과의 타입은 std::ptrdiff_t 입니다.
- 만약 P 와 Q 가 모두 널 포인터 값 으로 평가되면, 결과는 0 입니다.
-
그렇지 않고
P
와
Q
가 동일한 배열 객체
x
의 각각
i번째와j번째 배열 요소를 가리키는 경우, 표현식 P - Q 의 값은 i − j 입니다.
-
- 만약 i − j 가 std::ptrdiff_t 로 표현 불가능할 경우, 동작은 정의되지 않습니다.
- 그렇지 않고, P 와 Q 가 동일한 완전 객체, 기본 클래스 하위 객체 또는 멤버 하위 객체를 가리키는 경우, 결과는 0 입니다.
- 그렇지 않은 경우, 동작은 정의되지 않습니다.
이러한 포인터 산술 연산자를 통해 포인터는 LegacyRandomAccessIterator 요구 사항을 충족할 수 있습니다.
덧셈과 뺄셈의 경우,
P
또는
Q
가 "(cv 한정자가 있을 수 있는)
T
에 대한 포인터" 타입을 가지고,
T
와 배열 요소 타입이
유사(similar)
하지 않을 경우, 그 동작은 정의되지 않습니다:
int arr[5] = {1, 2, 3, 4, 5}; unsigned int *p = reinterpret_cast<unsigned int*>(arr + 1); unsigned int k = *p; // 정상, "k"의 값은 2입니다 unsigned int *q = p + 1; // 정의되지 않은 동작: "p"는 unsigned int가 아닌 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
는
usual arithmetic conversions
가
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) | ||||||||
승법 연산자는 다른 모든 이항 산술 연산자보다 높은 우선순위 를 가집니다. 이 연산자들은 왼쪽에서 오른쪽으로 결합합니다:
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 는 변환된 피연산자(들)를 가리킵니다.
- NaN과 임의의 숫자를 곱하면 NaN이 됩니다.
- 무한대와 0을 곱하면 NaN이 되고 FE_INVALID 가 발생합니다.
- 한 피연산자가 NaN인 경우, 결과는 NaN입니다.
- 0이 아닌 숫자를 ±0.0으로 나누면 올바른 부호의 무한대가 생성되고 FE_DIVBYZERO 가 발생합니다.
- 0.0을 0.0으로 나누면 NaN이 생성되고 FE_INVALID 가 발생합니다.
참고: CWG issue 614 가 해결되기 전까지 ( N2757 ), 이항 연산자 %의 한쪽 또는 양쪽 피연산자가 음수인 경우 나머지의 부호는 구현에 따라 정의되었습니다. 이는 정수 나눗셈의 반올림 방향에 의존하기 때문입니다. std::div 함수는 이러한 경우에 잘 정의된 동작을 제공했습니다.
참고: 부동 소수점 나머지 연산에 대해서는 std::remainder 와 std::fmod 를 참조하십시오.
오버로드
사용자 정의 연산자에 대한 오버로드 해결
에서, 승격된 산술 타입 쌍
LA
와
RA
에 대해, 그리고 승격된 정수 타입 쌍
LI
와
RI
에 대해 다음 함수 시그니처들이 오버로드 해결에 참여합니다:
|
LRA 연산자
*
(
LA, RA
)
|
||
|
LRA 연산자
/
(
LA, RA
)
|
||
|
LRI 연산자
%
(
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) | ||||||||
비트 NOT 연산자는 모든 이항 산술 연산자보다 우선순위 가 더 높습니다. 오른쪽에서 왼쪽으로 결합합니다:
~a - b; // (~a) - b와 동일하며, ~(a - b)가 아님 ~c * d; // (~c) * d와 동일하며, ~(c * d)가 아님 ~-e; // ~(-e)와 동일함
~
다음에
type name
또는
decltype
specifier
(since C++11)
이 올 때 문법적 모호성이 존재합니다: 이는 operator~일 수도 있고
destructor
식별자의 시작일 수도 있습니다. 이 모호성은
~
를 operator~로 해석함으로써 해결됩니다.
~
는 operator~를 형성하는 것이 구문적으로 유효하지 않은 위치에서만 destructor 식별자를 시작할 수 있습니다.
다른 모든 비트 논리 연산자는 다른 모든 이항 산술 연산자보다 우선순위 가 낮습니다. 비트 AND는 비트 XOR보다 우선순위가 높으며, 비트 XOR은 비트 OR보다 우선순위가 높습니다. 이들은 왼쪽에서 오른쪽으로 결합합니다:
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의 보수입니다 (피연산자와 결과가 부호 없는 것으로 간주되는 경우).
오버로드
사용자 정의 연산자에 대한 오버로드 해결
에서, 승격된 정수형 타입 쌍
L
과
R
에 대해 다음 함수 시그니처들이 오버로드 해결에 참여합니다:
|
R operator~
(
R
)
|
||
|
LR operator
&
(
L, R
)
|
||
|
LR operator
^
(
L, R
)
|
||
|
LR operator
|
(
L, R
)
|
||
여기서
LR
는
usual arithmetic conversions
가
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) | ||||||||
비트 시프트 연산자는 비트 논리 연산자보다 우선순위 가 높지만, 덧셈 및 곱셈 연산자보다는 낮습니다. 이러한 연산자들은 왼쪽에서 오른쪽으로 결합합니다:
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
부호 있는 비음수
a
의 경우,
a * 2
b
음수 a 의 경우, a << b 의 동작은 정의되지 않습니다.
부호 없는
a
와 부호 있는 비음수
a
의 경우,
a
>>
b
의 값은
a/2
b
음수 a 의 경우, a >> b 의 값은 구현에 따라 정의됩니다(대부분의 구현에서는 산술 오른쪽 시프트를 수행하여 결과가 음수로 유지됩니다). |
(C++20 이전) |
|
a
<<
b
의 값은
a * 2
b
a
>>
b
의 값은
a/2
b
|
(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" // unsigned에서 오버플로우 발생 "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>
의
public member function)
|
|
|
복소수에 단항 연산자 적용
(function template) |
|
|
valarray의 각 요소에 단항 산술 연산자 적용
(
std::valarray<T>
의
public member function)
|
덧셈 연산자
|
(C++11)
|
time_point를 포함하는 덧셈 및 뺄셈 연산 수행
(함수 템플릿) |
|
duration을 인자로 사용하는 산술 연산 구현
(함수 템플릿) |
|
|
(C++20)
|
year_month_day
와 일정 년수 또는 월수를 더하거나 뺌
(함수) |
|
두 문자열, 문자열과
char
, 또는 문자열과
string_view
를 연결함
(함수 템플릿) |
|
|
반복자를 진행시키거나 감소시킴
(
std::reverse_iterator<Iter>
의 public 멤버 함수)
|
|
|
반복자를 진행시키거나 감소시킴
(
std::move_iterator<Iter>
의 public 멤버 함수)
|
|
|
두 복소수 값 또는 복소수와 스칼라에 대한 복소수 산술 연산 수행
(함수 템플릿) |
|
|
두 valarray의 각 요소에 이항 연산자를 적용하거나 valarray와 값에 적용
(함수 템플릿) |
곱셈 연산자
|
duration을 인자로 하는 산술 연산 구현
(함수 템플릿) |
|
|
두 복소수 값 또는 복소수와 스칼라에 대한 복소수 산술 연산 수행
(함수 템플릿) |
|
|
두 valarray의 각 요소 또는 valarray와 값에 이항 연산자 적용
(함수 템플릿) |
비트 논리 연산자
|
이진 AND, OR, XOR 및 NOT 연산 수행
(
std::bitset<N>
의 public member function)
|
|
|
bitset에 대한 이진 논리 연산 수행
(function template) |
|
|
valarray의 각 요소에 단항 산술 연산자 적용
(
std::valarray<T>
의 public member function)
|
|
|
두 valarray의 각 요소에 이항 연산자 적용, 또는 valarray와 값에 적용
(function template) |
비트 시프트 연산자
|
두 valarray의 각 요소에 이항 연산자를 적용하거나, valarray와 값에 적용
(함수 템플릿) |
|
|
이진 왼쪽 시프트와 오른쪽 시프트를 수행
(
std::bitset<N>
의 public 멤버 함수)
|
스트림 삽입/추출 연산자
표준 라이브러리 전체에서, 비트 시프트 연산자는 일반적으로 I/O 스트림( std:: ios_base & 또는 이를 상속받은 클래스들 중 하나)을 좌측 피연산자 및 반환 타입으로 오버로드됩니다. 이러한 연산자는 스트림 삽입 및 스트림 추출 연산자로 알려져 있습니다:
|
형식화된 데이터를 추출합니다
(
std::basic_istream<CharT,Traits>
의
public member function)
|
|
|
문자와 문자 배열을 추출합니다
(function template) |
|
|
형식화된 데이터를 삽입합니다
(
std::basic_ostream<CharT,Traits>
의
public member function)
|
|
|
문자 데이터를 삽입하거나 rvalue 스트림에 삽입합니다
(function template) |
|
|
복소수를 직렬화 및 역직렬화합니다
(function template) |
|
|
bitset의 스트림 입력 및 출력을 수행합니다
(function template) |
|
|
문자열에 대한 스트림 입력 및 출력을 수행합니다
(function template) |
|
|
(C++11)
|
의사 난수 엔진에 대한 스트림 입력 및 출력을 수행합니다
(function template) |
|
(C++11)
|
의사 난수 분포에 대한 스트림 입력 및 출력을 수행합니다
(function template) |
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 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 이슈 1504
의 해결이 배열 요소에 대한
포인터 연산 동작을 가리키는 타입과 배열 요소 타입이 최상위 수준이 아닌 곳에서 다른 cv-한정자를 가질 경우 정의되지 않도록 만듦 |
명확히 정의됨 |
| CWG 1971 | C++98 |
~
의 모호성 해결 규칙이
~X
(
0
)
와 같은
경우에 적용되는지 불명확했음 |
해당 규칙이 이러한 경우에 적용됨 |
| CWG 2419 | C++98 |
비-배열 객체에 대한 포인터는
&
연산자로
얻은 경우에만 크기 1인 배열의 첫 번째 요소에 대한 포인터로 취급됨 |
모든 비-배열 객체에 대한
포인터에 적용됨 |
| CWG 2626 | C++98 |
내장
operator~
의 결과가 적절한 정의 없이
단순히 '1의 보수'로 기술됨 |
결과가 기수-2 표현을
기준으로 명시됨 |
| CWG 2724 | C++20 | 산술 우측 시프트의 반올림 방향이 불명확했음 | 명확히 정의됨 |
| CWG 2853 | C++98 |
객체 끝을 지난 포인터에
정수를 더하거나 뺄 수 없었음 |
가능함 |
참고 항목
| 일반 연산자 | ||||||
|---|---|---|---|---|---|---|
| assignment |
increment
decrement |
arithmetic | logical | comparison |
member
access |
other |
|
a
=
b
|
++
a
|
+
a
|
!
a
|
a
==
b
|
a
[
...
]
|
함수 호출
a ( ... ) |
|
콤마
a, b |
||||||
|
조건부 연산자
a ? b : c |
||||||
| 특수 연산자 | ||||||
|
static_cast
관련된 타입 간 변환을 수행
|
||||||
|
C 문서
for
Arithmetic operators
|