보통 산술 변환(Usual arithmetic conversions)
보통 산술 변환(Usual arithmetic conversions)
산술 타입이나 열거형 타입의 피연산자를 기대하는 이항 연산자를 많이 써 보면, int와 double을 더할 때처럼 서로 다른 타입이 만나면 자동으로 변환이 일어난다는 걸 느껴요. 이때 양쪽 피연산자를 어떤 공통 타입으로 맞추는 규칙이 필요한데, 이것이 바로 **보통 산술 변환(usual arithmetic conversions)**이에요. 같은 방식으로 변환과 결과 타입을 만들어 내고, 그 결과 타입이 곧 연산의 결과 타입이 돼요.
출처: cppreference
본문
정의(Definition)
보통 산술 변환은 다음과 같이 정의돼요.
1단계(Stage 1)
두 피연산자 모두에 lvalue-to-rvalue 변환을 적용해요. 그 결과로 생긴 prvalue가 나머지 과정에서 원래 피연산자 자리를 대신해요.
2단계(Stage 2)
- 두 피연산자 중 하나가 스코프 있는 열거형 타입이면 변환을 수행하지 않아요. 다른 피연산자가 같은 타입이 아니면 표현식은 잘못된 형태(ill-formed)예요.
- 그렇지 않으면 다음 단계로 진행해요. (C++11부터)
3단계(Stage 3)
- 두 피연산자 중 하나가 열거형 타입이고, 다른 피연산자가 다른 열거형 타입이나 부동소수점 타입이면 표현식은 잘못된 형태예요.
- 그렇지 않으면 다음 단계로 진행해요. (C++26부터)
4단계(Stage 4)
- 두 피연산자 중 하나가 부동소수점 타입이면 다음 규칙을 적용해요.
- 두 피연산자가 같은 타입이면 더 이상 변환을 수행하지 않아요.
- 그렇지 않고 두 피연산자 중 하나가 부동소수점이 아닌 타입이면, 그 피연산자를 다른 피연산자의 타입으로 변환해요.
- 그렇지 않고 두 피연산자 타입의 부동소수점 변환 순위가 정렬되어 있고(since C++23) 같지 않으면, 부동소수점 변환 순위가 더 낮은 타입의 피연산자를 다른 피연산자의 타입으로 변환해요.
- 그렇지 않고 두 피연산자 타입의 부동소수점 변환 순위가 같으면, 부동소수점 변환 하위 순위(subrank)가 더 낮은 피연산자를 다른 피연산자의 타입으로 변환해요.
- 그렇지 않으면 표현식은 잘못된 형태예요. (C++23부터)
- 그렇지 않으면 두 피연산자 모두 정수 타입이므로 다음 단계로 진행해요.
5단계(Stage 5)
두 피연산자는 공통 타입 C로 변환돼요. T1과 T2를 (정수 승격 규칙에 따른) 피연산자의 승격된 타입이라 할 때, C를 정하려면 다음 규칙을 적용해요.
T1과T2가 같은 타입이면C는 그 타입이에요.- 그렇지 않고
T1과T2가 모두 부호 있는 정수 타입이거나 모두 부호 없는 정수 타입이면,C는 정수 변환 순위가 더 큰 타입이에요. - 그렇지 않으면
T1과T2중 하나는 부호 있는 정수 타입S, 다른 하나는 부호 없는 정수 타입U예요. 다음 규칙을 적용해요.U의 정수 변환 순위가S의 정수 변환 순위보다 크거나 같으면C는U예요.- 그렇지 않고
S가U의 모든 값을 표현할 수 있으면C는S예요. - 그렇지 않으면
C는S에 대응하는 부호 없는 정수 타입이에요.
한 피연산자가 열거형 타입이고 다른 피연산자가 다른 열거형 타입이나 부동소수점 타입이면 이 동작은 비권장(deprecated)이에요. (C++20부터, C++26까지)
정수 변환 순위(Integer conversion rank)
모든 정수 타입은 다음과 같이 정의되는 정수 변환 순위를 가져요.
char와signed char(char가 부호 있으면)를 제외한 두 부호 있는 정수 타입은 표현이 같아도 같은 순위를 가지지 않아요.- 부호 있는 정수 타입의 순위는 폭이 더 작은 부호 있는 정수 타입의 순위보다 커요.
- 다음 정수 타입들의 순위는 순서대로 감소해요.
long long(C++11부터)longintshortsigned char
- 부호 없는 정수 타입의 순위는 대응하는 부호 있는 정수 타입의 순위와 같아요.
- 모든 표준 정수 타입의 순위는 같은 폭의 확장 정수 타입의 순위보다 커요. (C++11부터)
bool의 순위는 모든 표준 정수 타입의 순위보다 작아요.- 부호화 문자 타입(
char,char8_t(C++20부터),char16_t,char32_t(C++11부터),wchar_t)의 순위는 각자의 기본 타입의 순위와 같아요. 즉:char의 순위는signed char와unsigned char의 순위와 같아요.char8_t의 순위는unsigned char의 순위와 같아요.char16_t의 순위는std::uint_least16_t의 순위와 같아요.char32_t의 순위는std::uint_least32_t의 순위와 같아요.
wchar_t의 순위는 구현 정의인 기본 타입의 순위와 같아요.- 같은 폭인 다른 확장 부호 있는 정수 타입에 대한 확장 부호 있는 정수 타입의 순위는 구현 정의지만, 정수 변환 순위를 정하는 다른 규칙의 적용을 받아요. (C++11부터)
- 모든 정수 타입
T1,T2,T3에 대해,T1이T2보다 큰 순위를 갖고T2가T3보다 큰 순위를 가지면T1은T3보다 큰 순위를 가져요.
정수 변환 순위는 정수 승격(integral promotion)의 정의에도 쓰여요.
부동소수점 변환 순위와 하위 순위(Floating-point conversion rank and subrank)
부동소수점 변환 순위
모든 부동소수점 타입은 다음과 같이 정의되는 부동소수점 변환 순위를 가져요.
long doubledoublefloat- 실수 타입
T의 순위는 그 값 집합이T의 값 집합의 진부분집합인 실수 타입의 순위보다 커요. - 값 집합이 같은 두 확장 부동소수점 타입은 같은 순위를 가져요.
- 정확히 하나의 cv-비한정 표준 부동소수점 타입과 같은 값 집합을 가진 확장 부동소수점 타입은 그 표준 부동소수점 타입과 같은 순위를 가져요.
- 둘 이상의 cv-비한정 표준 부동소수점 타입과 같은 값 집합을 가진 확장 부동소수점 타입은
double과 같은 순위를 가져요. (C++23부터)
부동소수점 변환 하위 순위
같은 부동소수점 변환 순위를 가진 부동소수점 타입들은 부동소수점 변환 하위 순위로 정렬돼요. 하위 순위는 같은 순위를 가진 타입들 사이에서 전순서(total order)를 이뤄요.
std::float16_t, std::float32_t, std::float64_t, std::float128_t(고정 폭 부동소수점 타입)는 같은 변환 순위를 가진 표준 부동소수점 타입보다 큰 변환 하위 순위를 가져요. 그 외의 경우 변환 하위 순위 순서는 구현 정의예요. (C++23부터)
활용(Usage)
부동소수점 변환 순위와 하위 순위는 다음에도 쓰여요.
- 서로 다른 부동소수점 타입 사이의 변환이 암시적일 수 있는지, 아니면 좁히는 변환(narrowing conversion)인지 결정
- 오버로드 해석에서 변환 시퀀스 구별
std::basic_istream::operator>>를 사용한 확장 부동소수점 타입 추출에서std::num_get::get()이 실제로 추출하는 타입 결정std::basic_ostream::operator<<를 사용한 확장 부동소수점 타입 삽입에서std::num_put::put()이 실제로 삽입하는 타입 결정 (C++23부터)std::complex의 변환 생성자가 explicit인지 결정- 서로 다른 부동소수점 타입의 인자를 공용 또는 특수 수학 함수에 전달할 때 공통 부동소수점 타입 결정
결함 보고(Defect reports)
이전에 발표된 C++ 표준들에 소급 적용된 동작 변경 결함 보고들이 있어요.
| DR | 적용 대상 | 발표 당시 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 1642 | C++98 | 보통 산술 변환이 lvalue를 포함할 수 있었음 | 먼저 lvalue-to-rvalue 변환을 적용 |
| CWG 2528 | C++20 | unsigned char와 unsigned int의 삼자 비교는 중간 정수 승격 때문에 잘못된 형태였음[1] |
실제로 피연산자를 승격하지 않고, 승격된 타입에 기반해 공통 타입을 결정[2] |
| CWG 2892 | C++98 | 두 피연산자가 같은 부동소수점 타입일 때 "더 이상 변환이 필요 없다"는 의미가 불분명했음 | "더 이상 변환을 수행하지 않는다"로 변경 |
- ↑ 해결 전에는
unsigned char가 5단계 시작에서int로 승격된 뒤unsigned int로 변환됐어요. 그런데 후자의 변환은 좁히는 변환이라 삼자 비교가 잘못된 형태가 됐어요. - ↑ 해결 후에도 공통 타입은 여전히
unsigned int예요. 차이점은unsigned char가 중간 정수 승격 없이unsigned int로 직접 변환된다는 거예요. 이 변환은 좁히는 변환이 아니므로 삼자 비교는 잘 형성돼요.
더 알아보기
- 정수 승격: 작은 정수 타입을
int이상으로 올리는 변환. - 암시적 변환: 타입이 자동으로 맞춰지는 변환 전반.
- 산술 연산자: 이 변환이 적용되는 주요 연산자들.