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 .

Если имя ссылается на более чем одну сущность, оно называется перегруженным , и компилятор должен определить, какую перегрузку вызвать. Проще говоря, вызывается та перегрузка, параметры которой наиболее точно соответствуют аргументам.

Подробно, разрешение перегрузки выполняется через следующие шаги:

  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);  // ошибка: неоднозначная перегрузка

Помимо вызовов функций, перегруженные имена функций могут появляться в нескольких дополнительных контекстах, где применяются другие правила: см. Address of an overloaded function .

Если функция не может быть выбрана при разрешении перегрузки, она не может быть использована (например, если это шаблонная сущность с неудавшимся ограничением ).

Содержание

Кандидатные функции

Перед началом разрешения перегрузки функции, выбранные поиском по имени и выводом шаблонных аргументов, объединяются в набор 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 ( ) , и каждое найденное объявление добавляется в набор кандидатных функций.
  • Для каждой не- explicit пользовательской функции преобразования в T или в базовом классе T (если не скрыта), чьи cv-квалификаторы совпадают или превышают cv-квалификаторы T , и где функция преобразования конвертирует в:
  • pointer-to-function
  • reference-to-pointer-to-function
  • reference-to-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 через указатель, возвращенный из функции преобразования

Вызов перегруженного оператора

Если хотя бы один из аргументов оператора в выражении имеет тип класса или тип перечисления, то в разрешении перегрузки участвуют как встроенные операторы , так и пользовательские перегрузки операторов , при этом набор функций-кандидатов выбирается следующим образом:

Для унарного оператора @ с аргументом типа T1 (после удаления cv-квалификаторов) или бинарного оператора @ с левым операндом типа T1 и правым операндом типа T2 (после удаления cv-квалификаторов) формируются следующие наборы кандидатов функций:

1) кандидаты-члены : если T1 является полным классом или классом, определяемым в текущий момент, множество кандидатов-членов представляет собой результат квалифицированного поиска имени для T1::operator@ . Во всех остальных случаях множество кандидатов-членов пусто.
2) non-member candidates : Для операторов, где перегрузка операторов допускает нечленные формы, все объявления, найденные с помощью поиска по неквалифицированному имени для operator@ в контексте выражения (что может включать ADL ), за исключением того, что объявления функций-членов игнорируются и не препятствуют продолжению поиска в следующей охватывающей области видимости. Если оба операнда бинарного оператора или единственный операнд унарного оператора имеют тип перечисления, то из набора найденных функций нечленными кандидатами становятся только те, параметр которых имеет этот тип перечисления (или ссылку на этот тип перечисления)
3) встроенные кандидаты : Для operator, , унарного operator & и operator - > набор встроенных кандидатов пуст. Для других операторов встроенные кандидаты перечислены на страницах встроенных операторов при условии, что все операнды могут быть неявно преобразованы к их параметрам. Если какой-либо встроенный кандидат имеет такой же список параметров, как и кандидат-не-член или переписанный кандидат-не-член (начиная с C++20) , не являющийся специализацией шаблона функции, он не добавляется в список встроенных кандидатов. При рассмотрении встроенных операторов присваивания преобразования из их первых параметров ограничены: рассматриваются только стандартные последовательности преобразований .
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 ! = .
Во всех случаях переписанные кандидаты не рассматриваются в контексте переписанного выражения. Для всех остальных операторов набор переписанных кандидатов пуст.
(since 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 <=> выбран разрешением перегрузки для оператора @ , x @ y интерпретируется как переписанное выражение: 0 @ ( y <=> x ) если выбранный кандидат является синтезированным кандидатом с обратным порядком параметров, или ( x <=> y ) @ 0 в противном случае, используя выбранный переписанный кандидат operator <=> .

Если кандидат переписанного operator == выбран разрешением перегрузки для оператора @ (который является либо == , либо != ), его возвращаемый тип должен быть (возможно, cv-квалифицированным) 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)

Инициализация конструктором

Когда объект классового типа прямо инициализируется или инициализируется по умолчанию (включая инициализацию по умолчанию в контексте copy-list-инициализации ) (начиная с C++11) , кандидатами являются все конструкторы инициализируемого класса. Список аргументов представляет собой список выражений инициализатора.

В противном случае, функциями-кандидатами являются все converting constructors инициализируемого класса. Список аргументов представляет собой выражение инициализатора.

Для default-initialization в контексте copy-list-initialization, если выбран explicit конструктор, инициализация является некорректной.

(since C++11)

Копирующая инициализация через преобразование

Если копирующая инициализация объекта классового типа требует вызова пользовательского преобразования для конвертации выражения инициализации типа cv S в тип cv T инициализируемого объекта, следующие функции являются функциями-кандидатами:

  • все converting constructors класса T
  • не- explicit функции преобразования из S и его базовых классов (если не скрыты) в T или класс, производный от T , или ссылку на такие. Если эта copy-инициализация является частью последовательности direct-инициализации cv T (инициализация ссылки для привязки к первому параметру конструктора, принимающего ссылку на cv T ), то также рассматриваются explicit функции преобразования.

В любом случае, список аргументов для целей разрешения перегрузки состоит из единственного аргумента, которым является выражение инициализации, которое будет сравниваться с первым аргументом конструктора или с неявным объектным аргументом функции преобразования.

Инициализация неклассовых типов через преобразование

Когда инициализация объекта неклассового типа cv1 T требует использования пользовательской функции преобразования для конвертации из инициализирующего выражения классового типа cv S , кандидатами являются следующие функции:

  • неявные пользовательские функции преобразования S и его базовых классов (если не скрыты), которые возвращают тип T или тип, преобразуемый в T с помощью стандартной последовательности преобразований , или ссылку на такой тип. Квалификаторы cv на возвращаемом типе игнорируются при выборе кандидатных функций.
  • если это прямая инициализация , также рассматриваются явные пользовательские функции преобразования S и его базовых классов (если не скрыты), которые возвращают тип T или тип, преобразуемый в T с помощью квалификационного преобразования , или ссылку на такой тип.

В любом случае, список аргументов для целей разрешения перегрузки состоит из единственного аргумента, которым является выражение инициализации, которое будет сравниваться с неявным объектным аргументом функции преобразования.

Инициализация ссылки преобразованием

Во время инициализации ссылки , когда ссылка на cv1 T привязывается к lvalue или rvalue результату преобразования из выражения инициализатора типа класса cv2 S , следующие функции выбираются в набор кандидатов:

  • Неявные пользовательские функции преобразования S и его базовых классов (если не скрыты) в тип
  • (при преобразовании в lvalue) lvalue-ссылка на cv2 T2
  • (при преобразовании в rvalue или lvalue функционального типа) cv2 T2 или rvalue-ссылка на cv2 T2
где cv2 T2 является reference-compatible с cv1 T .
  • При прямой инициализации явные пользовательские функции преобразования также рассматриваются, если T2 является тем же типом, что и T , или может быть преобразован в тип T с помощью квалификационного преобразования.

В любом случае, список аргументов для целей разрешения перегрузки состоит из единственного аргумента, которым является выражение инициализации, которое будет сравниваться с неявным объектным аргументом функции преобразования.

Инициализация списком

Когда объект типа неагрегированного класса T подвергается list-инициализации , происходит двухфазное разрешение перегрузки.

  • на этапе 1, кандидатами являются все конструкторы со списком инициализации типа T , а список аргументов для разрешения перегрузки состоит из единственного аргумента - списка инициализации
  • если разрешение перегрузки завершается неудачей на этапе 1, начинается этап 2, где кандидатами являются все конструкторы типа T , а список аргументов для разрешения перегрузки состоит из отдельных элементов списка инициализации

Если список инициализации пуст и T имеет конструктор по умолчанию, фаза 1 пропускается.

При копирующей инициализации списком, если на фазе 2 выбирается явный конструктор, инициализация является некорректной (в отличие от всех остальных видов копирующей инициализации, где явные конструкторы даже не рассматриваются).

Дополнительные правила для кандидатов шаблонов функций

Если поиск имени обнаружил шаблон функции, вывод аргументов шаблона и проверка любых явных аргументов шаблона выполняются для нахождения значений аргументов шаблона (если есть), которые могут быть использованы в данном случае:

  • Если оба успешны, аргументы шаблона используются для синтеза объявлений соответствующих специализаций шаблона функции, которые добавляются в набор кандидатов, и такие специализации обрабатываются так же, как нешаблонные функции, за исключением случаев, указанных в правилах разрешения неоднозначностей ниже.
  • Если вывод аргументов завершается неудачей или синтезированная специализация шаблона функции была бы некорректной, такая функция не добавляется в набор кандидатов (см. SFINAE ).

Если имя ссылается на одну или несколько шаблонов функций и также на набор перегруженных нешаблонных функций, эти функции и специализации, сгенерированные из шаблонов, являются всеми кандидатами.

См. перегрузку шаблонов функций для получения дополнительной информации.

Если шаблон конструктора или шаблон функции преобразования имеет условный спецификатор explicit , который оказывается value-dependent , то после вывода, если контекст требует кандидата, который не является explicit, и сгенерированная специализация является explicit, она удаляется из набора кандидатов.

(since C++20)

Дополнительные правила для кандидатов в конструкторы

Удаленные по умолчанию move constructors и move assignment operators исключаются из набора candidate functions.

Конструктор, унаследованный от типа класса C , у которого первый параметр имеет тип "ссылка на P " (включая такой конструктор, инстанцированный из шаблона), исключается из набора candidate functions при создании объекта типа D , если выполняются все следующие условия:

  • Список аргументов содержит ровно один аргумент.
  • C является reference-related к P .
  • P является reference-related к D .
(since C++11)

Дополнительные правила для кандидатов-функций-членов

Если любая кандидатная функция является функцией-членом (статической или нестатической) без явного параметра объекта (начиная с C++23) , но не конструктором, она рассматривается как имеющая дополнительный параметр ( неявный параметр объекта ), который представляет объект, для которого она вызывается, и появляется перед первым из фактических параметров.

Аналогично, объект, для которого вызывается функция-член, добавляется в начало списка аргументов как implied object argument .

Для функций-членов класса X тип неявного параметра объекта зависит от cv-квалификаторов и ref-квалификаторов функции-члена, как описано в разделе функции-члены .

Пользовательские функции преобразования рассматриваются как члены неявного аргумента объекта для определения типа неявного параметра объекта .

Функции-члены, введенные с помощью using-объявления в производный класс, считаются членами производного класса для целей определения типа неявного объектного параметра .

Для статических функций-членов считается, что неявный параметр объекта соответствует любому объекту: его тип не проверяется и для него не предпринимается попытка преобразования.

(until C++23)

Для оставшейся части разрешения перегрузки implied object argument неотличим от других аргументов, но следующие специальные правила применяются к implicit object parameter :

1) пользовательские преобразования не могут быть применены к неявному параметру объекта
2) rvalues могут быть привязаны к неконстантному неявному параметру объекта (если это не для функции-члена с квалификатором ссылки) (начиная с 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 параметров, но имеет параметр с многоточием , она является жизнеспособной.
3) Если функция-кандидат имеет более M параметров и M+1 -й параметр и все последующие параметры имеют аргументы по умолчанию, она является жизнеспособной. Для остальной части разрешения перегрузки список параметров обрезается на M .
4) Если функция имеет связанное ограничение , оно должно быть удовлетворено
(since C++20)
5) Для каждого аргумента должна существовать по крайней мере одна неявная последовательность преобразований, которая преобразует его в соответствующий параметр.
6) Если любой параметр имеет ссылочный тип, привязка ссылки учитывается на этом шаге: если rvalue-аргумент соответствует параметру неконстантной lvalue-ссылки или lvalue-аргумент соответствует параметру rvalue-ссылки, функция не является подходящей.

Пользовательские преобразования (как преобразующие конструкторы, так и пользовательские функции преобразования) запрещены в неявной последовательности преобразований, если это позволяет применить более одного пользовательского преобразования. В частности, они не рассматриваются, если целью преобразования является первый параметр конструктора или неявный параметр объекта пользовательской функции преобразования, и этот конструктор/пользовательское преобразование является кандидатом для

  • инициализация с помощью списковой инициализации, когда список инициализаторов содержит ровно один элемент, который сам является списком инициализаторов, и целью является первый параметр конструктора класса X , и преобразование выполняется в X или ссылку на (возможно cv-квалифицированный) X :
struct A { A(int); };
struct B { B(A); };
B b{{0}}; // list-initialization of B
// candidates: B(const B&), B(B&&), B(A)
// {0} -> B&& not viable: would have to call B(A)
// {0} -> const B&: not viable: would have to bind to rvalue, would have to call B(A)
// {0} -> A viable. Calls A(int): user-defined conversion to A is not banned
(начиная с C++11)

Наиболее подходящая функция

Для каждой пары подходящих функций F1 и F2 , неявные последовательности преобразований от i -го аргумента к i -му параметру ранжируются для определения, какая из них лучше (за исключением первого аргумента: неявный объектный аргумент для статических функций-членов не влияет на ранжирование)

F1 считается лучшей функцией, чем F2 , если неявные преобразования для всех аргументов F1 являются не хуже , чем неявные преобразования для всех аргументов F2, и

1) существует по крайней мере один аргумент F1 , чьё неявное преобразование лучше , чем соответствующее неявное преобразование для этого аргумента F2 , или, если это не так,
2) (только в контексте неклассовой инициализации через преобразование), стандартная последовательность преобразования из результата F1 в инициализируемый тип является лучше , чем стандартная последовательность преобразования из результата F2 , или, если это не так,
3) (только в контексте инициализации через функцию преобразования для прямой привязки ссылки к ссылочному типу функции), результат F1 является ссылкой того же вида (lvalue или rvalue), что и инициализируемая ссылка, а результат F2 не является, или, если это не так,
(since C++11)
4) F1 является функцией без шаблона, тогда как F2 является специализацией шаблона, или, если это не так,
5) F1 и F2 являются специализациями шаблонов, и F1 является более специализированной в соответствии с правилами частичного упорядочивания для специализаций шаблонов , или, если это не так,
6) F1 и F2 являются нешаблонными функциями, и F1 является более частично-упорядоченной по ограничениям , чем F2 :
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
}
, или, если это не так,
(since C++20)


7) F1 является конструктором класса D , F2 является конструктором базового класса B для D , и для всех аргументов соответствующие параметры F1 и F2 имеют одинаковый тип:
struct A
{
    A(int = 0);
};
struct B: A
{
    using A::A;
    B();
};
B b; // OK, B::B()
, или, если это не так,
(since 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);  // 1-й аргумент: &i → int* лучше чем &i → const int*
                  // 2-й аргумент: 1L → short и 1L → int эквивалентны
                  // вызов Fcn(int*, int)
    Fcn(&i, 'c'); // 1-й аргумент: &i → int* лучше чем &i → const int*
                  // 2-й аргумент: 'c' → int лучше чем 'c' → short
                  // вызов Fcn(int*, int)
    Fcn(&i, s);   // 1-й аргумент: &i → int* лучше чем &i → const int*
                  // 2-й аргумент: 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) Точное соответствие : преобразование не требуется, преобразование lvalue-to-rvalue, преобразование квалификаторов, преобразование указателей на функции, (начиная с C++17) пользовательское преобразование типа класса в тот же класс
2) Продвижение : целочисленное продвижение, продвижение с плавающей точкой
3) Преобразования : целочисленные преобразования, преобразования с плавающей точкой, преобразования между целыми числами и числами с плавающей точкой, преобразования указателей, преобразования указателей на члены, булевы преобразования, пользовательские преобразования производного класса в его базовый класс

Ранг последовательности стандартных преобразований является наихудшим из рангов стандартных преобразований, которые она содержит (может быть до трёх преобразований )

Привязка ссылочного параметра непосредственно к выражению аргумента представляет собой либо тождество, либо преобразование производного класса к базовому:

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&)

Поскольку ранжирование последовательностей преобразований работает только с типами и категориями значений, битовое поле может связываться с аргументом-ссылкой для целей ранжирования, но если эта функция будет выбрана, это приведёт к ошибке компиляции.

1) Стандартная последовательность преобразований всегда лучше , чем пользовательская последовательность преобразований или последовательность преобразований с многоточием.
2) Пользовательская последовательность преобразования всегда лучше , чем последовательность преобразования с многоточием .
3) Стандартная последовательность преобразований S1 является лучше , чем стандартная последовательность преобразований S2 , если
а) S1 является правильной подпоследовательностью S2 , исключая lvalue-преобразования; последовательность тождественного преобразования считается подпоследовательностью любого нетождественного преобразования, или, если это не так,
б) ранг S1 выше ранга S2 , или, если это не так,
c) оба S1 и S2 связываются с ссылочным параметром, отличным от неявного параметра объекта функции-члена с ref-квалификатором, и S1 связывает rvalue-ссылку с rvalue, в то время как S2 связывает lvalue-ссылку с rvalue:
int i;
int f1();
int g(const int&);  // перегрузка #1
int g(const int&&); // перегрузка #2
int j = g(i);    // lvalue int -> const int& - единственное допустимое преобразование
int k = g(f1()); // rvalue int -> const int&& лучше чем rvalue int -> const int&
или, если не это,
d) оба S1 и S2 связываются с ссылочным параметром, при этом S1 связывает lvalue-ссылку на функцию, а S2 связывает rvalue-ссылку на функцию:
int f(void(&)());  // перегрузка #1
int f(void(&&)()); // перегрузка #2
void g();
int i1 = f(g); // вызывает #1
или, если не это,
e) S1 и S2 отличаются только квалификационным преобразованием, и

квалификация cv результата S1 является правильным подмножеством квалификации cv результата S2 , и S1 не является устаревшим преобразованием строкового литерала из массива в указатель (до C++11) .

(до C++20)

результат S1 может быть преобразован в результат S2 с помощью квалификационного преобразования.

(начиная с C++20)
int f(const int*);
int f(int*);
int i;
int j = f(&i); // &i -> int* лучше чем &i -> const int*, вызывает f(int*)
или, если не это,
f) оба S1 и S2 связываются с ссылочными параметрами, отличающимися только cv-квалификацией верхнего уровня, и тип S1 имеет менее строгую cv-квалификацию, чем тип S2 :
int f(const int &); // перегрузка #1
int f(int &);       // перегрузка #2 (обе ссылки)
int g(const int &); // перегрузка #1
int g(int);         // перегрузка #2
int i;
int j = f(i); // lvalue i -> int& лучше чем lvalue int -> const int&
              // вызывает f(int&)
int k = g(i); // lvalue i -> const int& имеет ранг Exact Match
              // lvalue i -> rvalue int имеет ранг Exact Match
              // неоднозначная перегрузка: ошибка компиляции
или, если не это,
g) S1 и S2 связывают один и тот же ссылочный тип "ссылка на T " и имеют исходные типы V1 и V2 соответственно, где стандартная последовательность преобразований из V1 * в T * лучше, чем стандартная последовательность преобразований из V2 * в T * :
struct Z {};
struct A
{
    operator Z&();
    operator const Z&();  // перегрузка #1
};
struct B
{
    operator Z();
    operator const Z&&(); // перегрузка #2
};
const Z& r1 = A();        // OK, использует #1
const Z&& r2 = B();       // OK, использует #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 , если L1 инициализирует параметр std::initializer_list , тогда как L2 этого не делает.
void f1(int);                                 // #1
void f1(std::initializer_list<long>);         // #2
void g1() { f1({42}); }                       // выбирает #2
void f2(std::pair<const char*, const char*>); // #3
void f2(std::initializer_list<std::string>);  // #4
void g2() { f2({"foo", "bar"}); }             // выбирает #4
6) Последовательность инициализации списком L1 является лучше , чем последовательность инициализации списком L2 , если соответствующие параметры являются ссылками на массивы, и L1 преобразуется в тип "массив из N1 T", L2 преобразуется в "массив из N2 T", и N1 меньше N2.
(since C++11)
(until C++20)
6) Последовательность инициализации списком L1 является лучше , чем последовательность инициализации списком L2 , если соответствующие параметры являются ссылками на массивы, и L1 и L2 преобразуются в массивы одинакового типа элементов, и либо
  • количество элементов N1, инициализируемых L1, меньше количества элементов N2, инициализируемых L2, либо
  • N1 равно N2, и L2 преобразуется в массив неизвестной границы, а L1 - нет.
void f(int    (&&)[] ); // overload #1
void f(double (&&)[] ); // overload #2
void f(int    (&&)[2]); // overload #3
f({1});        // #1: Better than #2 due to conversion, better than #3 due to bounds
f({1.0});      // #2: double -> double is better than double -> int
f({1.0, 2.0}); // #2: double -> double is better than double -> int
f({1, 2});     // #3: -> int[2] is better than -> int[], 
               //     and int -> int is better than int -> double
(since C++20)

Если две последовательности преобразования неразличимы, поскольку они имеют одинаковый ранг, применяются следующие дополнительные правила:

1) Преобразование, не затрагивающее указатель на bool или указатель-на-член в bool , является более предпочтительным, чем то, которое затрагивает.
2) Преобразование, которое продвигает перечисление с фиксированным базовым типом к его базовому типу, является более предпочтительным, чем преобразование, которое продвигает к продвинутому базовому типу, если эти два типа различаются.
enum num : char { one = '0' };
std::cout << num::one; // '0', not 48
(since C++11)


3) Преобразование в любом направлении между типами с плавающей запятой FP1 и типами с плавающей запятой FP2 является лучше, чем преобразование в том же направлении между FP1 и арифметическим типом 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); // вызывает f(std::float32_t) в реализациях, где
              // float и std::float32_t имеют равные ранги преобразования
int j = f(y); // ошибка: неоднозначность, нет равного ранга преобразования
(начиная с C++23)
4) Преобразование, которое преобразует указатель-на-производный в указатель-на-базовый, лучше, чем преобразование указателя-на-производный в указатель-на- void , и преобразование указателя-на-базовый в void лучше, чем преобразование указателя-на-производный в void .
5) Если Mid наследуется (напрямую или косвенно) от Base , и Derived наследуется (напрямую или косвенно) от Mid
а) Derived * в Mid * лучше, чем Derived * в Base *
б) 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
      // неразличимы; компиляция завершается неудачей

Последовательность неявных преобразований при списковой инициализации

При list initialization аргументом является braced-init-list , который не является выражением, поэтому неявная последовательность преобразования в тип параметра для целей разрешения перегрузки определяется следующими специальными правилами:

  • Если тип параметра представляет собой некоторый агрегат X и список инициализации состоит ровно из одного элемента того же или производного класса (возможно, с cv-квалификаторами), неявная последовательность преобразования - это та, которая требуется для преобразования элемента в тип параметра.
  • В противном случае, если тип параметра является ссылкой на массив символов и список инициализации содержит один элемент, являющийся строковым литералом подходящего типа, неявная последовательность преобразования представляет собой тождественное преобразование.
  • В противном случае, если тип параметра - std:: initializer_list < X > , и существует несужающее неявное преобразование каждого элемента списка инициализации в X , неявная последовательность преобразования для целей разрешения перегрузки является наихудшим необходимым преобразованием. Если список в фигурных скобках пуст, последовательность преобразования представляет собой тождественное преобразование.
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 conv)
void g(A);
g({"foo", "bar"}); // выбирает #3 (lvalue const char[4] -> std::string: user-def conv)
  • В противном случае, если тип параметра — «массив из N элементов типа T» (это происходит только для ссылок на массивы), список инициализации должен содержать N или меньше элементов, и используется наихудшее неявное преобразование, необходимое для преобразования каждого элемента списка (или пустой пары фигурных скобок {} если список короче N) к типу T .
  • В противном случае, если тип параметра — «массив неизвестной границы T» (это происходит только для ссылок на массивы), используется худшее неявное преобразование, необходимое для преобразования каждого элемента списка в T .
(since C++20)
typedef int IA[3];
void h(const IA&);
void g(int (&&)[]);
h({1, 2, 3}); // преобразование int->int identity
g({1, 2, 3}); // то же самое, начиная с C++20
  • В противном случае, если тип параметра является неагрегированным классным типом X , разрешение перегрузки выбирает конструктор C класса X для инициализации из списка инициализации аргументов
  • Если C не является конструктором со списком инициализации и список инициализации содержит единственный элемент типа X с возможным cv-квалификатором, неявная последовательность преобразований имеет ранг Exact Match. Если список инициализации содержит единственный элемент типа, производного от X с возможным cv-квалификатором, неявная последовательность преобразований имеет ранг Conversion. (обратите внимание на отличие от агрегатов: агрегаты инициализируются напрямую из одноэлементных списков инициализации до рассмотрения aggregate initialization , неагрегаты рассматривают конструкторы initializer_list до любых других конструкторов)
  • в противном случае неявная последовательность преобразований является пользовательской последовательностью преобразований со второй стандартной последовательностью преобразований в виде тождественного преобразования.

Если несколько конструкторов являются подходящими, но ни один из них не лучше других, неявная последовательность преобразования является неоднозначной последовательностью преобразования.

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 является сужающим преобразованием, недопустимо в list-init
void f(B);
// f({'a', 'b'}); // f(A) и f(B) обе являются пользовательскими преобразованиями
  • В противном случае, если тип параметра является агрегатом, который может быть инициализирован из списка инициализации в соответствии с aggregate initialization , неявная последовательность преобразований является пользовательской последовательностью преобразований со второй стандартной последовательностью преобразований в виде тождественного преобразования.
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 *
несмотря на то, что первое устарело
ранг устаревшего преобразования
понижен (оно было
удалено в 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-квалификатором, не имела соответствующей суррогатной функции
имеет соответствующую
суррогатную функцию
CWG 1467 C++11 инициализация списком одного типа для
агрегатов и массивов была опущена
инициализация определена
CWG 1601 C++11 преобразование из enum в его базовый тип
не предпочитало фиксированный базовый тип
фиксированный тип предпочтительнее
того, во что он продвигается
CWG 1608 C++98 набор кандидатов-членов унарного оператора @
с аргументом типа T1 был пустым, если
T1 является классом, который определяется в данный момент
набор является результатом
квалифицированного поиска имени
T1::operator@ в этом случае
CWG 1687 C++98 когда встроенный кандидат выбирается разрешением перегрузки,
операнды подвергались преобразованию без ограничений
преобразовывать только операнды классового типа,
и отключить вторую
стандартную последовательность преобразований
CWG 2052 C++98 некорректно сформированные синтезированные специализации шаблонов функций могли
добавляться в набор кандидатов, делая программу некорректно сформированной
они не добавляются
в набор кандидатов
CWG 2076 C++11 пользовательское преобразование применяется к единственному инициализатору
во вложенном списке инициализации при list-инициализации
вследствие разрешения CWG issue 1467
не применено
CWG 2137 C++11 конструкторы списка инициализации проигрывают копирующим
конструкторам при list-инициализации 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 разрешение перегрузки для инициализации по умолчанию в контексте
copy-list-инициализации рассматривало только преобразующие конструкторы
рассматривает все конструкторы
CWG 2919 C++98 набор кандидатов при инициализации ссылки преобразованием
зависел от целевого типа инициализации
зависит от целевого
типа преобразования
P2468R2 C++20 кандидаты с переписыванием на основе operator == добавляются
для a ! = b даже если существует соответствующий operator ! =
не добавлены
  1. Тип первого параметра встроенного оператора присваивания — «lvalue-ссылка на возможно volatile-квалифицированный T ». Ссылки такого типа не могут быть привязаны к временному объекту.

Ссылки

  • Стандарт 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]

Смотрите также