제네릭 선택
제네릭 선택 (Generic selection, C11부터)
제어 표현식(controlling expression)의 타입에 따라, 컴파일 시간에 여러 표현식 중 하나를 고르는 방법을 제공해요.
출처: cppreference
본문
문법 (Syntax)
(C11부터) _Generic ( controlling-expression , association-list )
여기서 association-list는 쉼표로 구분된 연관(association)들의 목록이고, 각 연관은 다음 문법을 가져요.
type-name : expressiondefault : expression
여기서
| 용어 | 설명 |
|---|---|
| type-name | 가변 수정되지 않은 완전한 객체 타입이에요 (즉, VLA나 VLA에 대한 포인터가 아닌 타입). |
| controlling-expression | ( 쉼표 연산자가 아닌) 어떤 표현식이든 가능해요. default 연관을 쓰지 않으면 그 타입은 type-name 중 하나와 호환되어야 해요. |
| expression | ( 쉼표 연산자가 아닌) 어떤 타입·값 범주의 표현식이든 가능해요. |
association-list의 두 type-name이 호환 타입을 지정할 수는 없어요. default 키워드를 쓰는 연관은 하나만 있을 수 있어요. default를 쓰지 않는데 type-name 중 어느 것도 제어 표현식의 타입과 호환되지 않으면, 그 프로그램은 컴파일되지 않아요.
설명 (Explanation)
먼저, 제어 표현식의 타입은 lvalue 변환을 거쳐요. 이 변환은 타입 영역에서만 일어나요. 제어 표현식 타입의 최상위 cvr-한정자와 원자성을 버리고 배열→포인터/함수→포인터 변환을 적용해요. 부수 효과를 시작하거나 값을 계산하지는 않아요.
변환 후의 타입을 association-list의 type-name들과 비교해요.
타입이 어떤 연관의 type-name과 호환되면, 제네릭 선택의 타입·값·값 범주는 그 type-name에 대해 콜론 뒤에 나타나는 표현식의 그것들과 같아요.
type-name 중 어느 것도 제어 표현식의 타입과 호환되지 않고 default 연관이 제공되면, 제네릭 선택의 타입·값·값 범주는 default : 라벨 뒤의 표현식의 그것들과 같아요.
참고 (Notes)
제어 표현식과 선택되지 않은 선택들의 표현식은 결코 평가되지 않아요.
lvalue 변환 때문에, "abc"는 char[4]가 아니라 char*와 매칭돼요. 또 (int const){0}는 const int가 아니라 int와 매칭돼요.
함수 지정자와 void 표현식을 포함한 모든 값 범주가 제네릭 선택의 표현식으로 허용돼요. 선택되면 제네릭 선택 자체도 같은 값 범주를 가져요.
C99에서 도입된 <tgmath.h>의 타입 제네릭 수학 매크로는 컴파일러 특정 방식으로 구현됐어요. C11에서 도입된 제네릭 선택은 프로그래머가 비슷한 타입 의존 코드를 쓸 수 있게 해 줬어요.
제네릭 선택은 C++의 오버로딩과 비슷해요(컴파일 시간에 인자 타입에 따라 여러 함수 중 하나를 고르는 것). 차이는 제네릭 선택이 임의의 표현식 사이에서 고른다는 점이에요.
예시
#include <math.h>
#include <stdio.h>
// Possible implementation of the tgmath.h macro cbrt
#define cbrt(X) _Generic((X), \
long double: cbrtl, \
default: cbrt, \
float: cbrtf \
)(X)
int main(void)
{
double x = 8.0;
const float y = 3.375;
printf("cbrt(8.0) = %f\n", cbrt(x)); // selects the default cbrt
printf("cbrtf(3.375) = %f\n", cbrt(y)); // converts const float to float,
// then selects cbrtf
}
출력:
cbrt(8.0) = 2.000000
cbrtf(3.375) = 1.500000
결함 보고 (Defect reports)
| DR | 적용 대상 | 발표된 동작 | 올바른 동작 |
|---|---|---|---|
| DR 481 | C11 | 제어 표현식이 lvalue 변환을 거치는지가 명세가 불충분했다 | 거친다 |
더 알아보기
<tgmath.h>의 타입 제네릭 수학 매크로 문서를 보면 제네릭 선택이 실제로 어디서 쓰이는지 알 수 있어요.- C++의 함수 오버로딩과 비교하면 제네릭 선택의 위치가 더 분명해져요.
- cppreference의 제네릭 선택 원문에서 최신 표준 변경을 확인할 수 있어요.