Namespaces
Variants

Overload resolution

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

함수 호출을 컴파일하기 위해 컴파일러는 먼저 name lookup 을 수행해야 하며, 함수의 경우 argument-dependent lookup 을 포함할 수 있고, 함수 템플릿의 경우 template argument deduction 이 뒤따를 수 있습니다.

이름이 둘 이상의 개체를 참조하는 경우, 이를 오버로드된(overloaded) 이라고 하며, 컴파일러는 어떤 오버로드를 호출할지 결정해야 합니다. 간단히 말해, 매개변수가 인수와 가장 밀접하게 일치하는 오버로드가 호출됩니다.

자세히 설명하면, 오버로드 해결은 다음 단계를 통해 진행됩니다:

  1. candidate functions 집합 구축.
  2. viable functions 로만 집합 정리.
  3. 단일 best viable function 결정을 위한 집합 분석 ( ranking of implicit conversion sequences 포함 가능).
void f(long);
void f(float);
f(0L); // f(long) 호출
f(0);  // 오류: 모호한 오버로드

함수 호출 외에도, 오버로드된 함수 이름은 여러 추가적인 상황에서 나타날 수 있으며, 여기에는 다른 규칙들이 적용됩니다: 오버로드된 함수의 주소 를 참조하십시오.

함수가 오버로드 해결에 의해 선택될 수 없다면, 사용할 수 없습니다 (예: 실패한 constraint 를 가진 templated entity 인 경우).

목차

후보 함수들

오버로드 해결이 시작되기 전에, 이름 검색과 템플릿 인자 추론으로 선택된 함수들이 결합되어 candidate functions 집합을 형성합니다. 구체적인 세부 사항은 오버로드 해결이 이루어지는 컨텍스트에 따라 달라집니다.

명명된 함수 호출

만약 E 함수 호출 표현식 E ( args ) 에서 오버로드된 함수 집합 및/또는 함수 템플릿(호출 가능 객체는 제외)을 지칭하는 경우, 다음 규칙들이 적용됩니다:

  • 표현식 E PA - > B 또는 A. B 형태를 가질 경우 (여기서 A 가 클래스 타입 cv T 를 가짐), B T 의 멤버 함수로 조회됩니다 . 이 조회를 통해 발견된 함수 선언들이 후보 함수들입니다. 오버로드 해결을 위한 인자 목록은 타입 cv T 의 암시적 객체 인자를 가집니다.
  • 표현식 E 기본 표현식 인 경우, 이름은 함수 호출에 대한 일반 규칙( ADL 을 포함할 수 있음)에 따라 조회됩니다 . 이 조회를 통해 발견된 함수 선언들은 (조회 방식 때문에) 다음 중 하나입니다:
  • 모든 비멤버 함수 (이 경우 오버로드 해결을 위한 인수 목록은 함수 호출 표현식에서 사용된 인수 목록과 정확히 일치함)
  • 어떤 클래스 T 의 모든 멤버 함수. 이 경우 this 가 스코프 내에 있고 T 또는 T 의 파생 클래스에 대한 포인터인 경우, * this 가 암시적 객체 인수로 사용됨. 그렇지 않은 경우 ( this 가 스코프 내에 없거나 T 를 가리키지 않는 경우), T 타입의 가상 객체가 암시적 객체 인수로 사용되며, 이후 오버로드 해결이 비정적 멤버 함수를 선택하면 프로그램은 올바르지 않음.

클래스 객체 호출

만약 E 함수 호출 표현식 E ( args ) 에서 클래스 타입 cv T 를 가지면,

  • T 의 함수 호출 연산자들은 operator ( ) 이름에 대한 일반적인 조회 를 통해 얻어지며, 발견된 모든 선언은 후보 함수 집합에 추가됩니다.
  • T 또는 T 의 기반 클래스에 있는 (숨겨지지 않은) 각 비- explicit 사용자 정의 변환 함수 에 대해, 해당 cv 한정자가 T 의 cv 한정자와 동일하거나 더 많고, 변환 함수가 다음으로 변환하는 경우:
  • pointer-to-function
  • reference-to-pointer-to-function
  • reference-to-function
그런 다음 서로게이트 호출 함수(surrogate call function) 가 고유한 이름으로 생성되어 후보 함수 집합에 추가됩니다. 이 함수의 첫 번째 매개변수는 변환 결과이며, 나머지 매개변수는 변환 결과가 허용하는 매개변수 목록이고, 반환 타입은 변환 결과의 반환 타입입니다. 이 서로게이트 함수가 이후의 오버로드 해결 과정에서 선택되면, 사용자 정의 변환 함수가 호출된 후 변환 결과가 호출됩니다.

어떤 경우든, 오버로드 해결을 위한 인수 목록은 묵시적 객체 인수 E 가 앞에 오는 함수 호출 표현식의 인수 목록입니다 (대리 함수와 매칭할 때, 사용자 정의 변환은 묵시적 객체 인수를 자동으로 대리 함수의 첫 번째 인수로 변환합니다).

int f1(int);
int f2(float);
struct A
{
    using fp1 = int(*)(int);
    operator fp1() { return f1; } // 함수 포인터로의 변환 함수
    using fp2 = int(*)(float);
    operator fp2() { return f2; } // 함수 포인터로의 변환 함수
} a;
int i = a(1); // 변환 함수를 통해 반환된 포인터로 f1 호출

오버로드된 연산자 호출

표현식 내 연산자의 인수 중 적어도 하나가 클래스 타입이나 열거형 타입을 가지는 경우, 내장 연산자 사용자 정의 연산자 오버로드 모두 오버로드 해결에 참여하며, 후보 함수 집합은 다음과 같이 선택됩니다:

단항 연산자 @ 의 인수가 (cv 한정자를 제거한 후) T1 타입을 가지거나, 이항 연산자 @ 의 왼쪽 피연산자가 T1 타입이고 오른쪽 피연산자가 T2 타입일 경우 (cv 한정자를 제거한 후), 다음 후보 함수 집합들이 준비됩니다:

1) 멤버 후보 : T1 가 완전한 클래스이거나 현재 정의 중인 클래스인 경우, 멤버 후보 집합은 qualified name lookup 의 결과인 T1::operator@ 입니다. 다른 모든 경우에는 멤버 후보 집합이 비어 있습니다.
2) 비멤버 후보 : 연산자 오버로딩 이 비멤버 형태를 허용하는 연산자의 경우, 표현식의 컨텍스트에서 비정규화된 이름 조회 를 통해 발견된 모든 선언들(이때 ADL 이 관여할 수 있음). 단, 멤버 함수 선언은 무시되며 조회가 다음 포괄 범위로 계속되는 것을 방해하지 않음. 이항 연산자의 두 피연산자 모두 또는 단항 연산자의 유일한 피연산자가 열거형 타입을 가질 경우, 조회 집합에서 비멤버 후보가 되는 함수는 매개변수가 해당 열거형 타입(또는 해당 열거형 타입에 대한 참조)을 가진 함수들만 해당됨
3) 내장 후보 함수(built-in candidates) : operator, , 단항 operator & , 그리고 operator - > 연산자의 경우 내장 후보 집합은 비어 있습니다. 다른 연산자들의 경우 내장 후보는 내장 연산자 페이지 에 나열된 함수들로, 모든 피연산자가 해당 매개변수로 암시적으로 변환될 수 있는 경우에 한합니다. 만약 내장 후보가 비멤버 후보 또는 재작성된 비멤버 후보 (C++20부터) 와 동일한 매개변수 목록을 가지고 있고, 해당 비멤버 후보가 함수 템플릿 특수화가 아닌 경우, 그 내장 후보는 내장 후보 목록에 추가되지 않습니다. 내장 대입 연산자들이 고려될 때, 첫 번째 매개변수로의 변환은 제한됩니다: 표준 변환 순서(standard conversion sequences) 만이 고려됩니다.
4) 재작성된 후보들 :
  • 네 가지 관계 연산자 표현식 x < y , x <= y , x > y , 그리고 x >= y 에 대해, 발견된 모든 멤버, 비멤버, 내장 operator <=> 들이 집합에 추가됩니다.
  • 네 가지 관계 연산자 표현식 x < y , x <= y , x > y , x >= y 그리고 삼항 비교 표현식 x <=> y 에 대해, 발견된 각 멤버, 비멤버, 내장 operator <=> 에 대해 두 매개변수의 순서가 반전된 합성 후보가 추가됩니다.
  • x ! = y 에 대해, 일치하는 operator ! = 이 없는 경우, 발견된 모든 멤버, 비멤버, 내장 operator == 들이 집합에 추가됩니다.
  • 동등 연산자 표현식 x == y x ! = y 에 대해, 일치하는 operator ! = 이 없는 경우, 발견된 각 멤버, 비멤버, 내장 operator == 에 대해 두 매개변수의 순서가 반전된 합성 후보가 추가됩니다.
모든 경우에, 재작성된 후보들은 재작성된 표현식의 문맥에서 고려되지 않습니다. 다른 모든 연산자에 대해 재작성된 후보 집합은 비어 있습니다.
(C++20부터)

오버로드 해결을 위해 제출되는 후보 함수 집합은 위의 집합들의 합집합입니다. 오버로드 해결을 위한 인수 목록은 연산자의 피연산자들로 구성되지만, operator-> 의 경우 두 번째 피연산자는 함수 호출을 위한 인수가 아닙니다( 멤버 접근 연산자 참조).

struct A
{
    operator int();              // 사용자 정의 변환
};
A operator+(const A&, const A&); // 비멤버 사용자 정의 연산자
void m()
{
    A a, b;
    a + b; // 멤버 후보: 없음
           // 비멤버 후보: operator+(a, b)
           // 내장 후보: int(a) + int(b)
           // 오버로드 해결이 operator+(a, b)를 선택함
}

오버로드 해결이 내장 후보를 선택하는 경우, 클래스 타입의 피연산자로부터의 사용자 정의 변환 시퀀스 는 두 번째 표준 변환 시퀀스를 가질 수 없습니다: 사용자 정의 변환 함수는 직접 예상된 피연산자 타입을 제공해야 합니다:

struct Y { operator int*(); }; // Y는 int*로 변환 가능
int *a = Y() + 100.0;          // 오류: 포인터와 double 간의 operator+ 없음

operator, 연산자에 대해, operator & 단항 연산자, 그리고 operator - > 연산자의 경우, 후보 함수 집합에 사용 가능한 함수가 없는 경우(아래 참조), 해당 연산자는 내장 연산자로 재해석됩니다.

재작성된 operator <=> 후보가 연산자 @ 에 대한 오버로드 해결로 선택된 경우, x @ y 는 재작성된 표현식으로 해석됩니다: 0 @ ( y <=> x ) (선택된 후보가 매개변수 순서가 반전된 합성 후보인 경우) 또는 ( x <=> y ) @ 0 (그렇지 않은 경우), 선택된 재작성된 operator <=> 후보를 사용합니다.

재작성된 operator == 후보가 연산자 @ ( == 또는 != )에 대한 오버로드 해결로 선택된 경우, 그 반환 타입은 (possibly cv-qualified) bool 이어야 하며, x @ y 는 재작성된 표현식으로 해석됩니다: y == x 또는 ! ( y == x ) (선택된 후보가 매개변수 순서가 반전된 합성 후보인 경우) 또는 ! ( x == y ) (그렇지 않은 경우), 선택된 재작성된 operator == 후보를 사용합니다.

이 경우의 오버로드 해결은 최종 순위 결정에서 재작성되지 않은 후보를 재작성된 후보보다 선호하며, 비합성 재작성 후보를 합성 재작성 후보보다 선호합니다.

이 반전된 인수 순서를 사용한 조회는 operator <=> ( std:: string , const char * ) operator == ( std:: string , const char * ) 만 작성하여 std::string const char * 간의 모든 양방향 비교를 생성할 수 있게 합니다. 자세한 내용은 기본 비교 를 참조하십시오.

(C++20부터)

생성자를 통한 초기화

클래스 타입의 객체가 직접 초기화(direct-initialized) 되거나 기본 초기화(default-initialized) 될 때 ( 복사 목록 초기화(copy-list-initialization) 문맥에서의 기본 초기화 포함) (C++11부터) , 후보 함수들은 초기화되는 클래스의 모든 생성자들입니다. 인자 목록은 초기화자의 표현식 목록입니다.

그렇지 않으면, 후보 함수들은 모두 초기화 중인 클래스의 converting constructors 입니다. 인수 목록은 초기화자의 표현식입니다.

복사-목록-초기화 문맥에서 기본 초기화 시, 만약 explicit 생성자가 선택되면, 초기화는 잘못된 형식입니다.

(C++11부터)

변환에 의한 복사 초기화

만약 클래스 타입 객체의 copy-initialization 에서 사용자 정의 변환을 호출하여 타입 cv S 의 초기화 표현식을 초기화되는 객체의 타입 cv T 로 변환해야 하는 경우, 다음 함수들이 후보 함수입니다:

  • T 의 모든 변환 생성자
  • S 와 그 기본 클래스들에서 T 로, 또는 T 에서 파생된 클래스로, 또는 그러한 클래스에 대한 참조로의 explicit 이 아닌 변환 함수들 (숨겨지지 않은 경우). 만약 이 복사 초기화가 cv T 의 직접 초기화 시퀀스의 일부라면 ( cv T 에 대한 참조를 취하는 생성자의 첫 번째 매개변수에 바인딩될 참조를 초기화하는 경우), 명시적 변환 함수들도 고려됩니다.

어떤 경우든, 오버로드 해결을 위한 인수 목록은 초기화 표현식인 단일 인수로 구성되며, 이는 생성자의 첫 번째 인수와 비교되거나 변환 함수의 암시적 객체 인수와 비교됩니다.

비클래스 변환 초기화

비클래스 타입 cv1 T 객체의 초기화가 클래스 타입 cv S 의 초기화 표현식으로부터 변환하기 위해 사용자 정의 변환 함수 를 필요로 할 때, 다음 함수들이 후보가 됩니다:

  • S 와 그 기반 클래스들의 (숨겨지지 않은) 비명시적 사용자 정의 변환 함수들 중 표준 변환 순서 를 통해 T 타입이나 T 로 변환 가능한 타입, 또는 그러한 타입에 대한 참조자를 생성하는 함수들. 후보 함수 선택 시 반환 타입의 cv 한정자는 무시됩니다.
  • 이것이 직접 초기화 인 경우, S 와 그 기반 클래스들의 (숨겨지지 않은) 명시적 사용자 정의 변환 함수들 중 자격 변환 을 통해 T 타입이나 T 로 변환 가능한 타입, 또는 그러한 타입에 대한 참조자를 생성하는 함수들도 고려됩니다.

어떤 경우든, 오버로드 해결을 위한 인수 목록은 초기화 표현식인 단일 인수로 구성되며, 이는 변환 함수의 암시적 객체 인수와 비교됩니다.

변환에 의한 참조 초기화

참조 초기화 과정에서 cv1 T 참조가 클래스 타입 cv2 S 의 초기화 표현식 변환 결과인 lvalue 또는 rvalue에 바인딩될 때, 다음 함수들이 후보 집합으로 선택됩니다:

  • S 와 그 기반 클래스들의 (숨겨지지 않은) 명시적이지 않은 사용자 정의 변환 함수가 해당 타입으로 변환하는 경우
  • (lvalue로 변환 시) lvalue reference to cv2 T2
  • (rvalue 또는 함수 타입의 lvalue로 변환 시) cv2 T2 또는 rvalue reference to cv2 T2
여기서 cv2 T2 reference-compatible with cv1 T 입니다.
  • 직접 초기화의 경우, 명시적 사용자 정의 변환 함수들도 T2 T 와 동일한 타입이거나 한정 변환(qualification conversion)을 통해 T 타입으로 변환될 수 있을 때 고려됩니다.

어떤 경우든, 오버로드 해결을 위한 인수 목록은 초기화 표현식인 단일 인수로 구성되며, 이는 변환 함수의 암시적 객체 인수와 비교됩니다.

목록 초기화

비-집합체(non-aggregate) 클래스 타입 T 의 객체가 목록 초기화(list-initialized) 될 때, 두 단계 오버로드 해결(two-phase overload resolution)이 수행됩니다.

  • 1단계에서 후보 함수들은 T 의 모든 initializer-list 생성자이며, 오버로드 해결을 위한 인수 목록은 단일 initializer list 인수로 구성됩니다.
  • 1단계에서 오버로드 해결이 실패하면 2단계로 진행되며, 이때 후보 함수들은 T 의 모든 생성자들이고, 오버로드 해결을 위한 인수 목록은 initializer list의 개별 요소들로 구성됩니다.

초기화자 목록이 비어 있고 T 가 기본 생성자를 가지고 있다면, 1단계는 건너뜁니다.

복사 목록 초기화에서, 2단계가 명시적 생성자를 선택하면 초기화는 올바르지 않은 형태가 됩니다 (명시적 생성자가 고려되지도 않는 다른 모든 복사 초기화와는 대조적으로).

함수 템플릿 후보에 대한 추가 규칙

이름 탐색이 함수 템플릿을 찾은 경우, 템플릿 인수 추론 및 명시적 템플릿 인수 확인이 수행되어 이 경우에 사용할 수 있는 템플릿 인수 값(있는 경우)을 찾습니다:

  • 둘 다 성공하면, 템플릿 인자는 해당 함수 템플릿 특수화의 선언을 합성하는 데 사용되며, 이러한 특수화는 후술할 결점 해결 규칙에서 달리 명시된 경우를 제외하고 비템플릿 함수와 동일하게 취급됩니다.
  • 인자 추론이 실패하거나 합성된 함수 템플릿 특수화가 올바르지 않은 형태일 경우, 해당 함수는 후보 집합에 추가되지 않습니다 ( SFINAE 참조).

이름이 하나 이상의 함수 템플릿을 참조하고 동시에 오버로드된 비템플릿 함수들의 집합을 참조하는 경우, 해당 함수들과 템플릿으로부터 생성된 특수화들은 모두 후보가 됩니다.

자세한 내용은 function template overloading 을 참조하십시오.

생성자 템플릿이나 변환 함수 템플릿이 조건부 explicit 지정자 를 가지고 있고 이것이 값에 의존적인 경우, 추론 후에 컨텍스트가 explicit이 아닌 후보를 요구하고 생성된 특수화가 explicit일 때, 해당 후보는 후보 집합에서 제거됩니다.

(since C++20)

생성자 후보에 대한 추가 규칙

삭제된 것으로 정의된 기본 제공 이동 생성자 이동 할당 연산자 는 후보 함수 집합에서 제외됩니다.

클래스 타입 C 로부터 상속된 생성자 중 첫 번째 매개변수가 "reference to P " 타입인 경우(템플릿에서 인스턴스화된 이러한 생성자 포함)는 D 타입의 객체를 생성할 때 다음 조건이 모두 충족되면 후보 함수 집합에서 제외됩니다:

  • 인수 목록에 정확히 하나의 인수가 있습니다.
  • C P 참조 관련 입니다.
  • P D 와 참조 관련입니다.
(C++11부터)

멤버 함수 후보에 대한 추가 규칙

만약 어떤 후보 함수가 멤버 함수 (정적 또는 비정적)이고 명시적 객체 매개변수 를 가지고 있지 않지만 (C++23부터) 생성자가 아닌 경우, 해당 함수는 호출 대상 객체를 나타내는 추가 매개변수( 암시적 객체 매개변수 )를 가지고 있는 것처럼 처리되며, 이 매개변수는 실제 매개변수들의 첫 번째 앞에 나타납니다.

마찬가지로, 멤버 함수가 호출되는 객체는 인수 목록 앞에 implied object argument 로 추가됩니다.

클래스 X 의 멤버 함수에 대해, 암시적 객체 매개변수의 타입은 멤버 함수 에서 설명된 대로 멤버 함수의 cv 한정자와 ref 한정자의 영향을 받습니다.

사용자 정의 변환 함수들은 묵시적 객체 매개변수 의 타입을 결정하기 위한 목적으로 암시적 객체 인수 의 멤버로 간주됩니다.

using 선언을 통해 파생 클래스에 도입된 멤버 함수들은 암시적 객체 매개변수의 타입을 정의하는 목적상 파생 클래스의 멤버로 간주됩니다 .

정적 멤버 함수의 경우, 암시적 객체 매개변수 는 모든 객체와 일치하는 것으로 간주됩니다: 해당 유형은 검사되지 않으며 이를 위한 변환 시퀀스가 시도되지 않습니다.

(until C++23)

나머지 오버로드 해결 과정에서 묵시적 객체 인자 는 다른 인자들과 구분되지 않지만, 다음 특별 규칙들이 암시적 객체 매개변수 에 적용됩니다:

1) 사용자 정의 변환은 암시적 객체 매개변수에 적용할 수 없습니다
2) rvalue는 비상수 암시적 객체 매개변수에 바인딩될 수 있음 (이것이 ref-qualified 멤버 함수를 위한 것이 아닌 경우) (C++11부터) 그리고 암시적 변환의 순위에 영향을 주지 않습니다.
struct B { void f(int); };
struct A { operator B&(); };
A a;
a.B::f(1); // 오류: 사용자 정의 변환은 암시적 객체 매개변수에
           // 적용될 수 없음
static_cast<B&>(a).f(1); // OK

적합 함수

위에서 설명한 대로 구성된 후보 함수 집합이 주어지면, 오버로드 해결의 다음 단계는 인수와 매개변수를 검사하여 이를 실행 가능 함수 집합으로 축소하는 것입니다.

후보 함수가 유효 함수 집합에 포함되려면 다음을 충족해야 합니다:

1) 인수가 M 개 있는 경우, 정확히 M 개의 매개변수를 가진 후보 함수는 실행 가능합니다
2) 후보 함수의 매개변수가 M 개보다 적지만 ellipsis parameter 를 가지고 있는 경우, 해당 함수는 적합합니다.
3) 후보 함수가 M 개 이상의 매개변수를 가지고 있으며, M+1 번째 매개변수와 그 이후의 모든 매개변수가 기본 인수를 가지고 있다면, 해당 함수는 적합합니다. 나머지 오버로드 해결 과정에서는 매개변수 목록이 M 번째에서 잘립니다.
4) 함수에 연관된 constraint 가 있는 경우, 이를 만족해야 함
(since C++20)
5) 모든 인수에 대해 해당 매개변수로 변환하는 적어도 하나의 암시적 변환 시퀀스가 존재해야 합니다.
6) 어떤 매개변수가 참조 타입을 가지는 경우, 이 단계에서 참조 바인딩이 고려됩니다: 만약 rvalue 인자가 비-const lvalue 참조 매개변수에 대응하거나, lvalue 인자가 rvalue 참조 매개변수에 대응하는 경우, 해당 함수는 적합하지 않습니다.

사용자 정의 변환(변환 생성자와 사용자 정의 변환 함수 모두)은 둘 이상의 사용자 정의 변환을 적용할 수 있게 하는 암시적 변환 시퀀스에 참여하는 것이 금지됩니다. 구체적으로, 변환 대상이 생성자의 첫 번째 매개변수이거나 사용자 정의 변환 함수의 암시적 객체 매개변수이고, 해당 생성자/사용자 정의 변환이 후보인 경우에는 고려되지 않습니다.

  • 초기화 목록이 정확히 하나의 요소를 가지며 그 요소 자체가 초기화 목록이고, 대상이 클래스 X 의 생성자의 첫 번째 매개변수이며, 변환이 X 또는 (가능한 cv-qualified) X 에 대한 참조로 이루어지는 목록 초기화에 의한 초기화:
struct A { A(int); };
struct B { B(A); };
B b{{0}}; // B의 목록 초기화
// 후보: B(const B&), B(B&&), B(A)
// {0} -> B&&: 적합하지 않음: B(A)를 호출해야 함
// {0} -> const B&: 적합하지 않음: rvalue에 바인딩해야 하며, B(A)를 호출해야 함
// {0} -> A: 적합함. A(int) 호출: A로의 사용자 정의 변환은 금지되지 않음
(C++11부터)

최적 실행 가능 함수

각각의 유효한 함수 쌍 F1 F2 에 대해, i 번째 인수에서 i 번째 매개변수로의 암시적 변환 시퀀스는 어느 것이 더 나은지 결정하기 위해 순위가 매겨집니다(정적 멤버 함수에 대한 암시적 객체 인수 인 첫 번째 인수는 순위 매기기에 영향을 주지 않습니다).

F1 F1 의 모든 인수에 대한 암시적 변환이 F2의 모든 인수에 대한 암시적 변환보다 나쁘지 않은 경우 F2 보다 더 나은 함수로 결정됩니다, 그리고

1) F1 의 인수 중 적어도 하나에 대해 해당 인수의 암시적 변환이 F2 의 해당 인수에 대한 암시적 변환보다 더 나은 경우, 또는 그렇지 않다면,
2) (비클래스 초기화 변환의 맥락에서만), F1 의 결과에서 초기화되는 타입으로의 표준 변환 시퀀스가 F2 의 결과에서의 표준 변환 시퀀스보다 더 나은 경우, 또는 그렇지 않은 경우,
3) (직접 참조 바인딩을 위한 변환 함수에 의한 초기화 문맥에서만 해당), F1 의 결과는 초기화되는 참조와 동일한 종류의 참조(좌측값 또는 우측값)이며, F2 의 결과는 그렇지 않거나, 만약 그렇지 않다면,
(C++11부터)
4) F1 는 비템플릿 함수인 반면 F2 는 템플릿 특수화이거나, 그렇지 않다면,
5) F1 F2 가 모두 템플릿 특수화이며 F1 템플릿 특수화에 대한 부분 순서 규칙 에 따라 더 특수화된 경우, 또는 그렇지 않다면,
6) F1 F2 가 비템플릿 함수이고 F1 부분 순서 제약 조건이 더 강한 경우:
template<typename T = int>
struct S
{
    constexpr void f(); // #1
    constexpr void f(this S&) requires true; // #2
};
void test()
{
    S<> s;
    s.f(); // calls #2
}
, 또는 그렇지 않은 경우,
(C++20부터)


7) F1 은 클래스 D 의 생성자이고, F2 D 의 기반 클래스 B 의 생성자이며, 모든 인수에 대해 F1 F2 의 해당 매개변수들이 동일한 타입을 가지는 경우:
struct A
{
    A(int = 0);
};
struct B: A
{
    using A::A;
    B();
};
B b; // OK, B::B()
, 또는 그렇지 않은 경우,
(C++11부터)


8) F2 가 재작성 후보이고 F1 은 그렇지 않은 경우, 또는 그렇지 않다면
9) F1 F2 가 모두 재작성 후보이며, F2 가 매개변수 순서가 반전된 합성 재작성 후보이고 F1 은 그렇지 않은 경우, 또는 그렇지 않다면
(since C++20)


10) F1 사용자 정의 추론 가이드 에서 생성되고 F2 는 그렇지 않은 경우, 또는 그렇지 않다면
11) F1 복사 추론 후보 이고 F2 는 그렇지 않은 경우, 또는 그렇지 않다면
12) F1 이 비템플릿 생성자에서 생성되고 F2 가 생성자 템플릿에서 생성된 경우:
template<class T>
struct A
{
    using value_type = T;
    A(value_type);  // #1
    A(const A&);    // #2
    A(T, T, int);   // #3
    template<class U>
    A(int, T, U);   // #4
};                  // #5 is A(A), the copy deduction candidate
A x(1, 2, 3); // uses #3, generated from a non-template constructor
template<class T>
A(T) -> A<T>;       // #6, less specialized than #5
A a (42); // uses #6 to deduce A<int> and #1 to initialize
A b = a;  // uses #5 to deduce A<int> and #2 to initialize
template<class T>
A(A<T>) -> A<A<T>>; // #7, as specialized as #5
A b2 = a; // uses #7 to deduce A<A<int>> and #1 to initialize
(C++17부터)

이러한 쌍별 비교는 모든 유효한 함수에 적용됩니다. 정확히 하나의 유효한 함수가 다른 모든 함수보다 우수할 경우, 오버로드 해결이 성공하고 이 함수가 호출됩니다. 그렇지 않으면 컴파일이 실패합니다.

void Fcn(const int*, short); // 오버로드 #1
void Fcn(int*, int);         // 오버로드 #2
int i;
short s = 0;
void f() 
{
    Fcn(&i, 1L);  // 첫 번째 인수: &i -> int*가 &i -> const int*보다 우수함
                  // 두 번째 인수: 1L -> short와 1L -> int는 동등함
                  // Fcn(int*, int) 호출
    Fcn(&i, 'c'); // 첫 번째 인수: &i -> int*가 &i -> const int*보다 우수함
                  // 두 번째 인수: 'c' -> int가 'c' -> short보다 우수함
                  // Fcn(int*, int) 호출
    Fcn(&i, s);   // 첫 번째 인수: &i -> int*가 &i -> const int*보다 우수함
                  // 두 번째 인수: s -> short가 s -> int보다 우수함
                  // 승자가 없어 컴파일 오류 발생
}

가장 적합한 함수가 여러 선언에서 발견된 함수로 결정되고, 이러한 선언들 중 어느 두 선언이 서로 다른 범위에 속하면서 함수를 적합하게 만드는 기본 인자를 지정하는 경우, 프로그램은 형식이 잘못되었습니다.

namespace A
{
    extern "C" void f(int = 5);
}
namespace B
{
    extern "C" void f(int = 5);
}
using A::f;
using B::f;
void use()
{
    f(3); // OK, 유효성 검사에 기본 인수가 사용되지 않음
    f();  // 오류: 기본 인수를 두 번 발견함
}

암시적 변환 시퀀스의 순위 결정

오버로드 해결에서 고려되는 인수-매개변수 묵시적 변환 시퀀스는 묵시적 변환 에 해당하며 복사 초기화 (비참조 매개변수의 경우)에서 사용되는 것과 동일합니다. 단, 묵시적 객체 매개변수나 대입 연산자의 좌변으로의 변환을 고려할 때는 임시 객체를 생성하는 변환은 고려되지 않습니다. 매개변수가 정적 멤버 함수의 묵시적 객체 매개변수인 경우, 묵시적 변환 시퀀스는 다른 어떤 표준 변환 시퀀스보다 나을 것도 없고 못할 것도 없는 표준 변환 시퀀스입니다. (C++23부터)

표준 변환 시퀀스 유형 에는 세 가지 등급 중 하나가 지정됩니다:

1) Exact match : 변환 불필요, lvalue-to-rvalue 변환, qualification 변환, function pointer 변환, (since C++17) 동일 클래스에 대한 사용자 정의 변환
2) 승격 : 정수 승격, 부동소수점 승격
3) 변환 : 정수 변환, 부동소수점 변환, 부동소수점-정수 변환, 포인터 변환, 멤버 포인터 변환, 부울 변환, 파생 클래스에서 기본 클래스로의 사용자 정의 변환

표준 변환 시퀀스의 순위는 그것이 포함하는 표준 변환들의 순위 중 가장 낮은 순위입니다 ( 최대 세 개의 변환 이 있을 수 있습니다)

참조 매개변수의 인수 표현식에 대한 직접 바인딩은 identity 변환이나 derived-to-base 변환 중 하나입니다:

struct Base {};
struct Derived : Base {} d;
int f(Base&);    // 오버로드 #1
int f(Derived&); // 오버로드 #2
int i = f(d); // d → Derived&는 Exact Match 순위
              // d → Base&는 Conversion 순위
              // f(Derived&) 호출

변환 순서의 순위 결정은 타입과 값 범주만으로 동작하기 때문에, 비트 필드 는 순위 결정 목적으로 참조 인자에 바인딩될 수 있지만, 해당 함수가 선택될 경우 ill-formed가 됩니다.

1) 표준 변환 시퀀스는 항상 더 나음 사용자 정의 변환 시퀀스나 생략 변환 시퀀스보다.
2) 사용자 정의 변환 시퀀스는 항상 더 나은 것으로 간주되며, ellipsis conversion 시퀀스보다 우선합니다.
3) 표준 변환 시퀀스 S1 이 표준 변환 시퀀스 S2 보다 더 나은 경우는 다음과 같다
a) S1 은(는) S2 의 적절한 부분 순서이며, 좌측값 변환은 제외됩니다; 항등 변환 순서는 모든 비-항등 변환의 부분 순서로 간주되거나, 그렇지 않은 경우,
b) S1 의 랭크가 S2 의 랭크보다 우수하거나, 그렇지 않을 경우
c) 둘 다 S1 S2 가 ref-qualified 멤버 함수의 암시적 객체 매개변수가 아닌 다른 것에 대한 참조 매개변수에 바인딩되고, S1 은 rvalue 참조를 rvalue에 바인딩하는 반면 S2 는 lvalue 참조를 rvalue에 바인딩하는 경우:
int i;
int f1();
int g(const int&);  // overload #1
int g(const int&&); // overload #2
int j = g(i);    // lvalue int -> const int& is the only valid conversion
int k = g(f1()); // rvalue int -> const int&& better than rvalue int -> const int&
또는, 그것이 아니라면,
d) 둘 다 S1 S2 가 참조 매개변수에 바인딩되고, S1 은 함수에 대한 lvalue 참조를 바인딩하는 반면 S2 는 함수에 대한 rvalue 참조를 바인딩합니다:
int f(void(&)());  // overload #1
int f(void(&&)()); // overload #2
void g();
int i1 = f(g); // calls #1
또는, 그것이 아니라면,
e) S1 S2 가 qualification conversion에서만 다르며,

S1 의 결과의 cv-qualification이 S2 의 결과의 cv-qualification의 proper subset이며 , S1 deprecated string literal array-to-pointer conversion 이 아닌 경우 (C++11까지) .

(C++20까지)

S1 의 결과가 qualification conversion을 통해 S2 의 결과로 변환될 수 있는 경우

(C++20부터)
int f(const int*);
int f(int*);
int i;
int j = f(&i); // &i -> int* is better than &i -> const int*, calls f(int*)
또는, 그것이 아니라면,
f) 둘 다 S1 S2 가 최상위 cv-한정자만 다르게 참조 매개변수에 바인딩되고, S1 의 타입이 S2 의 타입보다 cv-한정된 경우:
int f(const int &); // 오버로드 #1
int f(int &);       // 오버로드 #2 (둘 다 참조)
int g(const int &); // 오버로드 #1
int g(int);         // 오버로드 #2
int i;
int j = f(i); // 좌측값 i -> int&가 좌측값 int -> const int&보다 더 나음
              // f(int&) 호출
int k = g(i); // 좌측값 i -> const int&는 Exact Match 등급
              // 좌측값 i -> 우측값 int는 Exact Match 등급
              // 모호한 오버로드: 컴파일 오류
또는, 그렇지 않다면,
g) S1 S2 가 동일한 참조 타입 " T 에 대한 참조"를 바인딩하고, 각각 소스 타입 V1 V2 를 가지며, 이때 V1 * 에서 T * 로의 표준 변환 순서가 V2 * 에서 T * 로의 표준 변환 순서보다 더 나은 경우:
struct Z {};
struct A
{
    operator Z&();
    operator const Z&();  // overload #1
};
struct B
{
    operator Z();
    operator const Z&&(); // overload #2
};
const Z& r1 = A();        // OK, uses #1
const Z&& r2 = B();       // OK, uses #2
4) 사용자 정의 변환 시퀀스 U1 이 사용자 정의 변환 시퀀스 U2 보다 더 나은 경우 는 둘이 동일한 생성자/사용자 정의 변환 함수를 호출하거나 동일한 클래스를 집합체 초기화로 초기화할 때이며, 두 경우 모두 U1 의 두 번째 표준 변환 시퀀스가 U2 의 두 번째 표준 변환 시퀀스보다 더 나을 때입니다.
struct A
{
    operator short(); // user-defined conversion function
} a;
int f(int);   // overload #1
int f(float); // overload #2
int i = f(a); // A -> short, followed by short -> int (rank Promotion)
              // A -> short, followed by short -> float (rank Conversion)
              // calls f(int)
5) 목록 초기화 시퀀스 L1 이 목록 초기화 시퀀스 L2 보다 더 나은(better) 경우는 L1 std::initializer_list 매개변수를 초기화하는 반면 L2 는 그렇지 않을 때입니다.
void f1(int);                                 // #1
void f1(std::initializer_list<long>);         // #2
void g1() { f1({42}); }                       // chooses #2
void f2(std::pair<const char*, const char*>); // #3
void f2(std::initializer_list<std::string>);  // #4
void g2() { f2({"foo", "bar"}); }             // chooses #4
6) 목록 초기화 시퀀스 L1 이 목록 초기화 시퀀스 L2 보다 더 나은 경우는, 해당 매개변수가 배열에 대한 참조이고 L1이 "N1 T 배열" 타입으로 변환되며 L2가 "N2 T 배열"로 변환되고 N1이 N2보다 작을 때입니다.
(C++11부터)
(C++20까지)
6) 목록 초기화 시퀀스 L1 이 목록 초기화 시퀀스 L2 보다 더 나은 경우는, 해당 매개변수가 배열에 대한 참조이고 L1과 L2가 동일한 요소 타입의 배열로 변환되며 다음 중 하나를 만족할 때입니다:
  • L1으로 초기화된 요소 수 N1이 L2로 초기화된 요소 수 N2보다 작거나,
  • N1이 N2와 같고 L2는 알 수 없는 경계의 배열로 변환되지만 L1은 그렇지 않은 경우
void f(int    (&&)[] ); // overload #1
void f(double (&&)[] ); // overload #2
void f(int    (&&)[2]); // overload #3
f({1});        // #1: 변환으로 인해 #2보다 나음, 경계로 인해 #3보다 나음
f({1.0});      // #2: double -> double이 double -> int보다 나음
f({1.0, 2.0}); // #2: double -> double이 double -> int보다 나음
f({1, 2});     // #3: -> int[2]가 -> int[]보다 나음, 
               //     그리고 int -> int가 int -> double보다 나음
(C++20부터)

두 변환 시퀀스가 동일한 순위를 가져 구분할 수 없는 경우, 다음 추가 규칙이 적용됩니다:

1) bool 또는 bool 에 대한 멤버 포인터를 포함하지 않는 변환이 이를 포함하는 변환보다 더 우수합니다.
2) 고정된 기반 타입을 가진 열거형 을 해당 기반 타입으로 승격하는 변환은, 두 타입이 서로 다른 경우 승격된 기반 타입으로 승격하는 변환보다 우선합니다.
enum num : char { one = '0' };
std::cout << num::one; // '0', not 48
(C++11부터)


3) 부동소수점 타입 FP1 과 부동소수점 타입 FP2 사이의 양방향 변환은 다음 조건에서 FP1 과 산술 타입 T3 사이의 동일 방향 변환보다 우수합니다:
  • FP1 부동소수점 변환 순위 FP2 의 순위와 동일하고, 다음 중 하나를 만족하는 경우:
    • T3 가 부동소수점 타입이 아니거나,
    • T3 가 부동소수점 타입이지만 그 순위가 FP1 의 순위와 동일하지 않거나,
    • FP2 부동소수점 변환 하위순위 T3 의 하위순위보다 큰 경우
int f(std::float32_t);
int f(std::float64_t);
int f(long long);
float x;
std::float16_t y;
int i = f(x); // float와 std::float32_t의 변환 순위가 동일한 구현에서
              // f(std::float32_t)를 호출
int j = f(y); // 오류: 모호함, 동일한 변환 순위가 없음
(C++23부터)
4) 파생 클래스 포인터에서 기본 클래스 포인터로의 변환은 파생 클래스 포인터에서 void 포인터로의 변환보다 우수하며, 기본 클래스 포인터에서 void 포인터로의 변환은 파생 클래스 포인터에서 void 포인터로의 변환보다 우수합니다.
5) 만약 Mid Base 로부터 (직접적 또는 간접적으로) 파생되었고, Derived Mid 로부터 (직접적 또는 간접적으로) 파생된 경우
a) Derived * 에서 Mid * 로의 변환이 Derived * 에서 Base * 로의 변환보다 더 좋습니다
b) Derived 에서 Mid & 또는 Mid && 로의 변환이 Derived 에서 Base & 또는 Base && 로의 변환보다 더 나음
c) Base :: * 에서 Mid :: * 로의 변환은 Base :: * 에서 Derived :: * 로의 변환보다 더 나은 변환입니다.
d) Derived 에서 Mid 로의 변환이 Derived 에서 Base 로의 변환보다 더 좋습니다
e) Mid * 에서 Base * 로의 변환이 Derived * 에서 Base * 로의 변환보다 더 좋습니다
f) Mid 에서 Base & 또는 Base && 로의 변환이 Derived 에서 Base & 또는 Base && 로의 변환보다 더 나음
g) Mid :: * 에서 Derived :: * 로의 변환은 Base :: * 에서 Derived :: * 로의 변환보다 더 나은 변환입니다.
h) Mid 에서 Base 로의 변환이 Derived 에서 Base 로의 변환보다 더 좋습니다

모호한 변환 시퀀스는 사용자 정의 변환 시퀀스로 순위가 매겨집니다. 왜냐하면 인수에 대한 여러 변환 시퀀스가 존재하는 경우는 서로 다른 사용자 정의 변환을 포함할 때만 가능하기 때문입니다:

class B;
class A { A (B&);};         // 변환 생성자
class B { operator A (); }; // 사용자 정의 변환 함수
class C { C (B&); };        // 변환 생성자
void f(A) {} // 오버로드 #1
void f(C) {} // 오버로드 #2
B b;
f(b); // B -> A (생성자 통해) 또는 B -> A (함수 통해) (모호한 변환)
      // b -> C (생성자 통해) (사용자 정의 변환)
      // 오버로드 #1과 오버로드 #2에 대한 변환들이
      // 구분할 수 없음; 컴파일 실패

목록 초기화에서의 암시적 변환 시퀀스

목록 초기화 에서 인수는 braced-init-list 로, 표현식이 아니므로 오버로드 해결을 위한 매개변수 타입으로의 암시적 변환 순서는 다음 특별 규칙에 따라 결정됩니다:

  • 매개변수 타입이 어떤 집계체 X 이고 초기화 리스트가 동일하거나 파생된 클래스(possibly cv-qualified)의 정확히 하나의 원소로 구성된 경우, 암시적 변환 시퀀스는 해당 원소를 매개변수 타입으로 변환하는 데 필요한 변환입니다.
  • 그렇지 않고 매개변수 타입이 문자 배열에 대한 참조이고 초기화 리스트에 적절한 타입의 문자열 리터럴이 단일 원소로 있는 경우, 암시적 변환 시퀀스는 항등 변환입니다.
  • 그렇지 않고 매개변수 타입이 std:: initializer_list < X > 이고 초기화 리스트의 모든 원소에서 X 로의 비축소(non-narrowing) 암시적 변환이 존재하는 경우, 오버로드 해결을 위한 암시적 변환 시퀀스는 필요한 최악의 변환입니다. 중괄호 초기화 리스트가 비어 있는 경우, 변환 시퀀스는 항등 변환입니다.
struct A
{
    A(std::initializer_list<double>);          // #1
    A(std::initializer_list<complex<double>>); // #2
    A(std::initializer_list<std::string>);     // #3
};
A a{1.0, 2.0};     // #1 선택 (rvalue double → double: identity 변환)
void g(A);
g({"foo", "bar"}); // #3 선택 (lvalue const char[4] → std::string: 사용자 정의 변환)
  • 그렇지 않고 매개변수 유형이 "N T의 배열"인 경우(이는 배열에 대한 참조에서만 발생함), 초기화 목록은 N개 이하의 요소를 가져야 하며, 목록의 모든 요소(또는 목록이 N보다 짧은 경우 빈 중괄호 쌍 {} )를 T 로 변환하는 데 필요한 가장 나쁜 암시적 변환이 사용됩니다.
  • 그렇지 않고, 매개변수 타입이 "T의 알려지지 않은 경계 배열"인 경우(이는 배열에 대한 참조에서만 발생함), 목록의 모든 요소를 T 로 변환하는 데 필요한 가장 나쁜 암시적 변환이 사용됩니다.
(C++20부터)
typedef int IA[3];
void h(const IA&);
void g(int (&&)[]);
h({1, 2, 3}); // int→int 항등 변환
g({1, 2, 3}); // C++20부터 위와 동일
  • 그렇지 않고, 매개변수 타입이 비-집합 클래스 타입 X 인 경우, 오버로드 해결은 인수 초기화 목록으로부터 초기화하기 위해 X의 생성자 C를 선택합니다
  • C가 initializer-list 생성자가 아니고 초기화자 목록에 possibly cv-qualified X 타입의 단일 요소가 있는 경우, 암시적 변환 순서는 Exact Match 등급을 가집니다. 초기화자 목록에 possibly cv-qualified X에서 파생된 타입의 단일 요소가 있는 경우, 암시적 변환 순서는 Conversion 등급을 가집니다. (집계체와의 차이점 참고: 집계체는 aggregate initialization 을 고려하기 전에 단일 요소 초기화 목록에서 직접 초기화하는 반면, 비집계체는 다른 생성자들보다 먼저 initializer_list 생성자들을 고려합니다)
  • 그렇지 않은 경우, 암시적 변환 순서는 두 번째 표준 변환 순서가 identity conversion인 사용자 정의 변환 순서입니다.

여러 생성자가 사용 가능하지만 그 중 어느 것도 다른 것보다 더 나은 선택이 아닌 경우, 암시적 변환 시퀀스는 모호한 변환 시퀀스입니다.

struct A { A(std::initializer_list<int>); };
void f(A);
struct B { B(int, double); };
void g(B);
g({'a', 'b'});    // g(B(int, double)) 호출, 사용자 정의 변환
// g({1.0, 1,0}); // 오류: double->int 축소 변환, 목록 초기화에서 허용되지 않음
void f(B);
// f({'a', 'b'}); // f(A)와 f(B) 모두 사용자 정의 변환
  • 그렇지 않고, 매개변수 타입이 aggregate initialization 에 따라 초기화 리스트로부터 초기화될 수 있는 집합체(aggregate)인 경우, 암시적 변환 순서는 두 번째 표준 변환 순서가 항등 변환인 사용자 정의 변환 순서입니다.
struct A { int m1; double m2; };
void f(A);
f({'a', 'b'}); // f(A(int, double)) 호출, 사용자 정의 변환
  • 그렇지 않고 매개변수가 참조인 경우, 참조 초기화 규칙이 적용됩니다
struct A { int m1; double m2; };
void f(const A&);
f({'a', 'b'}); // 임시 객체 생성, f(A(int, double)) 호출. 사용자 정의 변환
  • 그렇지 않고 매개변수 타입이 클래스가 아니며 초기화자 리스트에 요소가 하나인 경우, 암시적 변환 순서는 해당 요소를 매개변수 타입으로 변환하는 데 필요한 변환 순서입니다.
  • 그렇지 않고 매개변수 타입이 클래스 타입이 아니며 초기화자 리스트에 요소가 없는 경우, 암시적 변환 순서는 항등 변환입니다.

인수가 지정 초기화 리스트이고 매개변수가 참조가 아닌 경우, 매개변수가 집합체 초기화 규칙에 따라 해당 초기화 리스트로 초기화될 수 있는 집합체 타입을 가진 경우에만 변환이 가능하며, 이 경우 암시적 변환 시퀀스는 두 번째 표준 변환 시퀀스가 항등 변환인 사용자 정의 변환 시퀀스입니다.

오버로드 해결 후 선택된 오버로드에 대해 집합체 멤버의 선언 순서가 일치하지 않으면 매개변수 초기화는 잘못된 형식이 됩니다.

struct A { int x, y; };
struct B { int y, x; };
void f(A a, int); // #1
void f(B b, ...); // #2
void g(A a);      // #3
void g(B b);      // #4
void h() 
{
    f({.x = 1, .y = 2}, 0); // OK; calls #1
    f({.y = 2, .x = 1}, 0); // error: selects #1, initialization of a fails
                            // due to non-matching member order
    g({.x = 1, .y = 2});    // error: ambiguous between #3 and #4
}


(C++20부터)

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 1 C++98 동일한 함수가 (다른 스코프에서) 서로 다른 기본
인자를 가진 상태로 선택되었을 때의 동작이
명시되지 않았음
이 경우 프로그램이
잘못된 형태임
CWG 83 C++98 문자열 리터럴에서
char * 로의 변환 시퀀스가
const char * 로의 변환 시퀀스보다 더 우수했음
비록 전자가 deprecated되었음에도 불구하고
deprecated 변환의 순위가 낮아짐
(해당 변환은 C++11에서
제거되었음)
CWG 162 C++98 F 에 의해 명명된 오버로드 집합이
&F(args) 의 경우에 비정적 멤버 함수를
포함하면 유효하지 않았음
이 경우에 오버로드 해결이
비정적 멤버 함수를 선택할 때만
유효하지 않음
CWG 233 C++98 참조와 포인터가 사용자 정의 변환과의
오버로딩 해결에서 일관되지 않게 처리됨
일관되게
처리됨
CWG 280 C++98 서로게이트 호출 함수가 접근 불가능한 기본 클래스에서
선언된 변환 함수의 후보 함수 집합에 추가되지 않았음
접근성 제약 조건을 제거하고,
서로게이트 호출 함수가 선택되었을 때
해당 변환 함수를 호출할 수 없는 경우
프로그램이 올바르지 않은 형태가 됨
CWG 415 C++98 함수 템플릿이 후보로 선택될 때,
해당 특수화들은 템플릿 인자 추론을 사용하여
인스턴스화되었음
이 경우 인스턴스화는 발생하지 않으며,
해당 선언들이 합성될 것임
CWG 495 C++98 인수에 대한 암시적 변환이 동등하게
좋을 때, 비템플릿 변환 함수가 항상
변환 함수 템플릿보다 더 나은 것으로 간주되었음.
후자가 더 나은 표준 변환 시퀀스를 가질 수 있음에도 불구하고
표준 변환
시퀀스가 전문화 수준보다
먼저 비교됨
CWG 1307 C++11 배열 크기에 기반한 오버로드 해결이 명시되지 않음 가능한 경우 더 짧은 배열이
더 나은 선택
CWG 1328 C++11 변환 결과에 대한 참조를 바인딩할 때
후보 함수 결정이 명확하지 않았음
명확하게 규정됨
CWG 1374 C++98 표준 변환 시퀀스를 비교할 때 참조 바인딩 전에
한정 변환 검사가 수행됨
반전됨
CWG 1385 C++11 ref-qualifier로 선언된 non-explicit 사용자 정의 변환 함수가
해당 서로게이트 함수를 가지지 않음
해당 서로게이트 함수를
가짐
CWG 1467 C++11 동일 타입 목록 초기화가
집합체와 배열에서 누락됨
초기화 정의됨
CWG 1601 C++11 열거형에서 해당 기반 타입으로의 변환 시
고정된 기반 타입을 우선하지 않음
고정 타입이 승격되는 타입보다
우선적으로 선택됨
CWG 1608 C++98 단항 연산자 @ 의 멤버 후보 집합이
인자의 타입이 T1 일 때 비어 있었음
T1 이 현재 정의 중인 클래스인 경우
이 경우 집합은
qualified name lookup의 결과인
T1::operator@
CWG 1687 C++98 오버로드 해결에서 내장 후보가 선택될 때,
피연산드들이 제한 없이 변환을 거침
클래스 타입 피연산드만 변환하고,
두 번째 표준 변환 시퀀스를
비활성화함
CWG 2052 C++98 잘못 형성된 합성 함수 템플릿 특수화가
후보 집합에 추가되어 프로그램이 잘못 형성될 수 있었음
후보 집합에 추가되지
않음
CWG 2076 C++11 중첩된 초기화 리스트에서 단일 초기화자를 대상으로 사용자 정의 변환 적용
목록 초기화 과정에서
CWG 이슈 1467 의 해결로 인해
적용되지 않음
CWG 2137 C++11 목록 초기화 시 복사 생성자에 밀려
무시되는 초기화 리스트 생성자 X { X } 에서
비집계체가 초기화 리스트를
우선적으로 고려함
CWG 2273 C++11 상속된 생성자와 비상속 생성자 간의
우선순위 결정 기준이 없었음
비상속 생성자가 우선함
CWG 2673 C++20 리라이트된 비멤버 후보와 동일한 매개변수 목록을 가진
내장 후보들이 내장 후보 목록에 추가됨
추가되지 않음
CWG 2712 C++98 내장 할당 연산자가 고려될 때,
첫 번째 매개변수가 임시 객체에 바인딩될 수 없었으나,
이는 이미 불가능한 상황이었음 [1]
중복 요구 사항
제거됨
CWG 2713 C++20 지정된 초기화자 목록에 관한 변환 제한이 매개변수가 참조인 경우에도 적용됨
이 경우에는 제한되지 않음
CWG 2789 C++23 명시적 객체 매개변수가 포함됨
매개변수-타입-목록 비교 시
제외됨
CWG 2856 C++11 복사-목록-초기화 컨텍스트에서 기본 초기화를 위한 오버로드 해결이
변환 생성자만 고려함
모든 생성자를 고려함
CWG 2919 C++98 변환에 의한 참조 초기화의 후보 집합이
초기화의 대상 타입에 의존함
변환의 대상
타입에 의존함
P2468R2 C++20 일치하는 operator ! = 가 존재하는 경우에도
a ! = b 를 위해 operator == 기반의 재작성 후보가 추가됨
추가되지 않음
  1. 내장 할당 연산자의 첫 번째 매개변수 타입은 "가능한 volatile 한정자를 가진 T 에 대한 lvalue 참조"입니다. 이러한 타입의 참조는 임시 객체에 바인딩될 수 없습니다.

참조문헌

  • C++23 표준 (ISO/IEC 14882:2024):
  • 12.2 오버로드 해결 [over.match]
  • C++20 표준(ISO/IEC 14882:2020):
  • 12.4 오버로드 해결 [over.match]
  • C++17 표준(ISO/IEC 14882:2017):
  • 16.3 오버로드 해결 [over.match]
  • C++14 표준(ISO/IEC 14882:2014):
  • 13.3 오버로드 해결 [over.match]
  • C++11 표준 (ISO/IEC 14882:2011):
  • 13.3 오버로드 해결 [over.match]
  • C++03 표준(ISO/IEC 14882:2003):
  • 13.3 오버로드 해결 [over.match]

참고 항목