오버로딩 해석이란 해당 호출 표현식에 대해 호출할 함수를 선택하는 과정으로, 이름을 호출하면 C++ 컴파일러는 추가 정보를 사용해 후보 함수들 중에서 하나를 선택해야 합니다. 대부분 호출 인자의 종류를 활용하여 선택합니다. 이는 C++ 표준화를 거치며 가장 잘 맞는 함수를 선택해야하기 때문에 복잡해졌습니다.
C.1 언제 오버로딩 해석이 적용되는가?
오버 로딩 해석은 함수 호출을 처리하는 과중 일부에 지나지 않으며 모든 함수가 적용되는 것도 아닙니다. 함수 포인터나 멤버 함수에 대한 포인터를 포인터에 의해 결정되므로 해석이 필요 없습니다. 두 번째로 함수 유사 매크로는 오버로딩 될 수 없으며 해석의 범주에 들어가지 않습니다. 함수에 대한 호출은 다음과 같이 이루어집니다.
- 이름을 룩업해 초기 오버로딩 집합을 형성
- 필요하다면 집합 재조정(템플릿 인자 연역과 치환으로 함수 템플릿 후보가 버려질 수 있습니다)
- 후보 함수가 해당 호출에 일치하지 않는다면 오버로딩 집합에서 삭제. 가용 함수 후보 집합 생성
- 오버로딩 해석을 통해 가장 잘 맞는 후보 선택. 다수 존재한다면 모호한 호출이 되어 오류
- 선택된 후보를 검사하여 오류가 발생한다면 메세지 출력
C.2 간략화된 오버로딩 해석
오버로딩 해석은 호출의 각 인자가 후보의 파라미터와 얼마나 잘 일치되는지를 갖고 가용 후보 함수들의 순위를 매깁니다. 더 나은 후보의 파라미터는 다른 후보의 파라미터보다 덜 일치해서는 안됩니다. 다음을 통해 순위를 매길 수 있습니다.
- 완벽한 일치 - 표현식의 형식 혹은 참조자(const, volatile 한정자 정도가 추가)의 형태인 경우
- 작은 수정을 통한 일치 - 배열에서 포인터, int**인자를 int const * const *등의 const 추가
- 형식 승격을 통한 일치 - int -> long 이나 float -> double 변환
- 표준 변환만을 사용해 일치 - 표준 변환이나 파생 클래스를 모호하지 않고 공개된 기본 클래스로 변환하여 호출. 암묵적 호출은 포함하지 않습니다.
- 사용자 정의 변환을 통한 일치 - 모든 종류의 암묵적 변환을 허용합니다.
- 줄임표를 통한 일치 - 줄임표 파라미터는 거의 모든 형식에 일치할 수 있습니다.
한 가지 예외로는 일반적이지 않은 복사 생성자를 갖는 클래스 형은 가능할 수도 가능하지 않을 수도 있습니다. 오버로딩 해석은 템플릿 인자 연역 이후에 일어나며, 연역 자체는 어떠한 종류의 변환으로도 간주되지 않습니다. 템플릿 인자 연역의 문맥에서, 템플릿 파라미터에 대한 rvalue 참조자는 해당 인자가 lvalue이면 lvalue 참조자형으로도 연역될 수 있고, 인자가 rvalue면 rvalue 참조자형으로 연역될 수도 있습니다.
정적이 아닌 멤버 함수에 대한 호출은 멤버 함수내에서 *this로 접근할 수 있는 숨겨진 파라미터를 갖습니다. Myclass의 멤버 함수에서 숨겨진 파라미터는 대개 Myclass&나 Myclass const&형을 갖습니다.
X형식의 인자와 완벽할 일치를 이루는 일반적인 파라미터는 네 종류로, X, X&, X const&, X&&가 존재합니다. 인자에 const가 붙지 않은 버전은 lvalue를, const가 붙은 버전은 rvalue를 선호합니다.
C.3 오버로딩 세부 사항
비템플릿 선호
다른 오버 로딩 해석 측면이 동일하다면 템플릿이 아닌 함수를 더 선호합니다. 두 템플릿 중 하나를 선택해야한다면 더 특수화된 템플릿을 선호합니다.
변환 순서
암묵적 변환은 기초 변환들의 순열로 이뤄집니다. 변환 순열 내에서는 사용자 정의 변환은 최대 한번만 일어날 수 있으며 표준 변환만 포함할수도 있습니다. 다른 변환 순열에 속하는 부분 변환 순열이 더 선호됩니다.
포인터 변환
포인터와 멤버에 대한 포인터는 다음과 같은 다양한 특수 표준 형식 변환을 거칩니다.
- bool형으로 변환
- 임의의 포인터형에서 void*로의 변환
- 포인터일 경우 상속받은 클래스에서 기본 클래스로의 변환
- 멤버에 대한 포인터인 경우 기본 클래스에서 상속받은 클래스로의 변환
이들은 모두 표준 변환만으로 일치를 만들어 내지만 같은 순위는 아닙니다. bool형으로의 변환은 다른 종류의 표준 변환보다도 더 안 좋은 것으로 취급합니다. 일반 포인터 변환일 경우 상속받은 클래스에서 기본 클래스로의 변환보다 void*형으로의 변환이 더 안 좋은 변환입니다. 이때 상속에 의한 관계가 있는 다른 클래스로의 변환이 가능하다면 가장 하위의 상속 클래스로의 변환을 선호합니다.
초기화자 목록
초기화자 목록 인자는 여러 가지 다양한 파라미터형으로 변환될 수 있습니다. 초기화자 목록의 형식을 암묵적 변환을 수행할 수도 있고, 집합 초기화를 통해 다른 인자를 만들어 낼 수도 있습니다. 초기화자로 초기화할 때의 오버로딩 해석 과정은 다음과 같습니다.
- 첫 번째 단계에서는 초기화자 목록 생성자만을 고려합니다. 즉, 기본이 아닌 파라미터 특정 형식 T에 대한 초기화자 생성자만을 고려합니다.
- 그런 생성자를 찾을 수 없다면 두번째 단계에서 그 외의 다른 모든 생성자를 고려합니다.
함자와 대리 함수
호출 표현식이 함수 대신 클래스형 객체를 참조한다면 재미있는 상황이 연출됩니다. 오버로딩 집합에는 두가지가 더 추가되늰데, 어떠한 멤버 연산자()라도 집합에 추가되며, 추가되는 클래스형 객체가 포인터를 함수형 암묵적으로 형식 변환하는 연산자가 있는 경우 입니다.
'C++공부 > C++ Templates' 카테고리의 다른 글
부록 E. 개념 (0) | 2022.07.08 |
---|---|
부록 B. 값 카테고리 (0) | 2022.07.08 |
부록 A. 단정의 법칙(One Definition Rule) (0) | 2022.07.08 |
28. 템플릿 디버깅 (0) | 2022.07.07 |
27. 표현식 템플릿 (0) | 2022.07.07 |