12. 오버로드 해석과 암시적 변환 순위, ADL
난이도 최상내 리뷰 · C++
해설 · C++
해설 — 오버로드 해석과 암시적 변환 순위, ADL
난이도: 최상
요약
컴파일러는 각 호출에서 ① 후보 함수를 모으고(이름 탐색 + ADL) ② 각 인자의 암시적 변환 순서를 매겨 ③ "모든 인자에서 더 나쁘지 않고 적어도 하나에서 더 나은" 유일한 최적 후보를 고른다. 동률이면 비템플릿이 템플릿을 이긴다. 실행 출력은 다음과 같다:
f(int)
f(double)
g(int)
g(void*)
h(bool)
k(int) non-template
k(T) template
game::emit(Vec) via ADL
핵심 개념 — 변환 순위
좋은 것부터: 정확한 일치(Exact) > 승격(Promotion) > 표준 변환(Conversion) > 사용자 정의 변환(User-defined) > 말줄임표(...). 오버로드 해석은 이 순위로 각 인자의 "암시적 변환 순서"를 비교한다.
단계별 내부 동작
- (1)
f(42)→f(int):42는int.f(int)는 정확한 일치,f(double)은 int→double 표준 변환. 정확한 일치가 이긴다. - (2)
f(3.14f)→f(double): 인자는float.float→double은 부동소수점 승격(promotion),float→int는 표준 변환(게다가 손실). 승격이 변환보다 위라f(double). - (3)
g(0)→g(int):0은int.g(int)정확한 일치.g(void*)는0을 널 포인터 상수로 본 포인터 변환(표준 변환). 정확한 일치 승. - (4)
g(nullptr)→g(void*):nullptr의 타입은std::nullptr_t.nullptr_t→void*는 허용되는 포인터 변환이지만,nullptr_t→int는 불가(정수로 못 바꾼다). 그래서g(int)는 후보에서 탈락하고g(void*)만 남는다. —0과nullptr이 다르게 동작하는 핵심 이유. - (5)
h("hello")→h(bool)⚠️ 함정:"hello"는const char[6]이고 배열-포인터 변환으로const char*가 된다. 후보는h(bool):const char*→bool은 포인터→bool 표준 변환(boolean conversion).h(const std::string&):const char*→std::string은std::string생성자를 통한 사용자 정의 변환. 표준 변환이 사용자 정의 변환보다 위이므로h(bool)이 선택된다. 의도와 정반대(문자열이true로!).std::string오버로드를 부르려면h(std::string("hello"))또는h("hello"s).
- (6)
k(42)→k(int)비템플릿:k(int)(정확)와k<int>(int)(정확) 둘 다 정확한 일치. 변환 순위가 같으면 비템플릿이 템플릿보다 우선한다. - (7)
k(42.0)→k(T)템플릿: 인자는double.k(int)는double→int변환(손실),k<double>(double)은 정확한 일치. 정확한 일치가 변환을 이기므로 이번엔 템플릿 승. (비템플릿 우선 규칙은 "변환 순위가 같을 때"만 적용된다.) - (8)
emit(v)→ ADL:emit는 호출 지점 스코프에 선언이 없지만, 인자v의 타입game::Vec이 속한 네임스페이스game을 인자 의존 탐색(ADL/Koenig lookup) 으로 함께 뒤져game::emit을 찾는다.std::cout << x,swap(a,b),begin(c)가 한정 없이 동작하는 것과 같은 메커니즘.
흔한 오해·함정
- "
std::string오버로드가 있으니 문자열 리터럴은 거기로 가겠지" → 아니다.const char*→bool(표준)이→std::string(사용자 정의)을 이긴다. C++11에서std::string직접 받는 함수에 리터럴을 넘길 때bool오버로드가 있으면 사고. (이래서 단일 인자 생성자에explicit, 그리고 위험한bool오버로드 회피가 권장된다.) - "
NULL과nullptr은 같다" →NULL은 보통0(정수)이라 정수 오버로드로 샐 수 있다.nullptr은nullptr_t라 포인터 오버로드로만 간다. - "템플릿이 더 구체적이니 우선" → 변환 순위가 우선이고, 동률일 때만 비템플릿 우선. 부분 순서(더 특수한 템플릿)는 그 다음 단계.
- ADL은 편의이자 함정: 의도치 않은 네임스페이스의 함수가 후보에 끼어들 수 있다(특히
std).
면접 포인트
- 변환 순위(Exact > Promotion > Conversion > User-defined)를 예로 들어 설명하고,
h("hello")가 왜bool로 가는지 말할 수 있는가. 0vsnullptr의 오버로드 차이와 그 이유(nullptr_t는 정수로 변환 불가).- 비템플릿 우선 규칙이 "변환 순위가 같을 때만" 적용된다는 점(
k(42)vsk(42.0)). - ADL이 무엇이며
swap/operator<<가 왜 한정 없이 동작하는지.