Overload resolution
Для компиляции вызова функции компилятор должен сначала выполнить name lookup , который для функций может включать argument-dependent lookup , а для шаблонов функций может сопровождаться template argument deduction .
Если имя ссылается на более чем одну сущность, оно называется перегруженным , и компилятор должен определить, какую перегрузку вызвать. Проще говоря, вызывается та перегрузка, параметры которой наиболее точно соответствуют аргументам.
Подробно, разрешение перегрузки выполняется через следующие шаги:
- Построение набора candidate functions .
- Сокращение набора до только viable functions .
- Анализ набора для определения единственной 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. Объявления функций, найденные при таком поиске, являются кандидатами. Список аргументов для разрешения перегрузки содержит неявный объектный аргумент типа cvT. - Если выражение 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-квалификаторов) формируются следующие наборы кандидатов функций:
T1
является полным классом или классом, определяемым в текущий момент, множество кандидатов-членов представляет собой результат
квалифицированного поиска имени
для
T1::operator@
. Во всех остальных случаях множество кандидатов-членов пусто.
operator@
в контексте выражения (что может включать
ADL
), за исключением того, что объявления функций-членов игнорируются и не препятствуют продолжению поиска в следующей охватывающей области видимости. Если оба операнда бинарного оператора или единственный операнд унарного оператора имеют тип перечисления, то из набора найденных функций нечленными кандидатами становятся только те, параметр которых имеет этот тип перечисления (или ссылку на этот тип перечисления)
|
4)
переписанные кандидаты
:
Во всех случаях переписанные кандидаты не рассматриваются в контексте переписанного выражения. Для всех остальных операторов набор переписанных кандидатов пуст.
|
(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
<=>
выбран разрешением перегрузки для оператора
Если кандидат переписанного
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, если выбран
|
(since C++11) |
Копирующая инициализация через преобразование
Если
копирующая инициализация
объекта классового типа требует вызова пользовательского преобразования для конвертации выражения инициализации типа
cv
S
в тип
cv
T
инициализируемого объекта, следующие функции являются функциями-кандидатами:
-
все
converting constructors
класса
T -
не-
explicitфункции преобразования изSи его базовых классов (если не скрыты) вTили класс, производный отT, или ссылку на такие. Если эта copy-инициализация является частью последовательности direct-инициализации cvT(инициализация ссылки для привязки к первому параметру конструктора, принимающего ссылку на cvT), то также рассматриваются 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-ссылка на cv2T2
-
(при преобразовании в lvalue) lvalue-ссылка на
cv2
-
где
cv2
T2является reference-compatible с cv1T.
-
При прямой инициализации явные пользовательские функции преобразования также рассматриваются, если
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.
Конструктор,
унаследованный
от типа класса
|
(since C++11) |
Дополнительные правила для кандидатов-функций-членов
Если любая кандидатная функция является функцией-членом (статической или нестатической) без явного параметра объекта (начиная с C++23) , но не конструктором, она рассматривается как имеющая дополнительный параметр ( неявный параметр объекта ), который представляет объект, для которого она вызывается, и появляется перед первым из фактических параметров.
Аналогично, объект, для которого вызывается функция-член, добавляется в начало списка аргументов как implied object argument .
Для функций-членов класса
X
тип неявного параметра объекта зависит от cv-квалификаторов и ref-квалификаторов функции-члена, как описано в разделе
функции-члены
.
Пользовательские функции преобразования рассматриваются как члены неявного аргумента объекта для определения типа неявного параметра объекта .
Функции-члены, введенные с помощью using-объявления в производный класс, считаются членами производного класса для целей определения типа неявного объектного параметра .
|
Для статических функций-членов считается, что неявный параметр объекта соответствует любому объекту: его тип не проверяется и для него не предпринимается попытка преобразования. |
(until C++23) |
Для оставшейся части разрешения перегрузки implied object argument неотличим от других аргументов, но следующие специальные правила применяются к implicit object parameter :
struct B { void f(int); }; struct A { operator B&(); }; A a; a.B::f(1); // Ошибка: пользовательские преобразования не могут быть применены // к неявному параметру объекта static_cast<B&>(a).f(1); // OK
Подходящие функции
Учитывая набор кандидатных функций, сформированный, как описано выше, следующим шагом разрешения перегрузки является анализ аргументов и параметров для сокращения набора до набора жизнеспособных функций
Чтобы быть включенной в набор пригодных функций, функция-кандидат должна удовлетворять следующим условиям:
M
аргументов, то кандидат функции, имеющий ровно
M
параметров, является жизнеспособным
M
параметров, но имеет
параметр с многоточием
, она является жизнеспособной.
M
параметров и
M+1
-й параметр и все последующие параметры имеют аргументы по умолчанию, она является жизнеспособной. Для остальной части разрешения перегрузки список параметров обрезается на
M
.
|
4)
Если функция имеет связанное
ограничение
, оно должно быть удовлетворено
|
(since C++20) |
Пользовательские преобразования (как преобразующие конструкторы, так и пользовательские функции преобразования) запрещены в неявной последовательности преобразований, если это позволяет применить более одного пользовательского преобразования. В частности, они не рассматриваются, если целью преобразования является первый параметр конструктора или неявный параметр объекта пользовательской функции преобразования, и этот конструктор/пользовательское преобразование является кандидатом для
- копирующая инициализация класса через пользовательское преобразование ,
- инициализация неклассового типа через функцию преобразования ,
- инициализация через функцию преобразования для прямой привязки ссылки ,
- инициализация через конструктор на втором шаге (прямой инициализации) копирующей инициализации класса,
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, и
F1
, чьё неявное преобразование
лучше
, чем соответствующее неявное преобразование для этого аргумента
F2
, или, если это не так,
F1
в инициализируемый тип является
лучше
, чем стандартная последовательность преобразования из результата
F2
, или, если это не так,
|
3)
(только в контексте инициализации через функцию преобразования для прямой привязки ссылки к ссылочному типу функции), результат
F1
является ссылкой того же вида (lvalue или rvalue), что и инициализируемая ссылка, а результат
F2
не является, или, если это не так,
|
(since C++11) |
F1
является функцией без шаблона, тогда как
F2
является специализацией шаблона, или, если это не так,
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) |
|
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)
Каждому типу стандартной последовательности преобразований присваивается один из трёх рангов:
Ранг последовательности стандартных преобразований является наихудшим из рангов стандартных преобразований, которые она содержит (может быть до трёх преобразований )
Привязка ссылочного параметра непосредственно к выражению аргумента представляет собой либо тождество, либо преобразование производного класса к базовому:
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&)
Поскольку ранжирование последовательностей преобразований работает только с типами и категориями значений, битовое поле может связываться с аргументом-ссылкой для целей ранжирования, но если эта функция будет выбрана, это приведёт к ошибке компиляции.
S1
является
лучше
, чем стандартная последовательность преобразований
S2
, если
S1
является правильной подпоследовательностью
S2
, исключая lvalue-преобразования; последовательность тождественного преобразования считается подпоследовательностью любого нетождественного преобразования, или, если это не так,
S1
выше ранга
S2
, или, если это не так,
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&
S1
и
S2
связываются с ссылочным параметром, при этом
S1
связывает lvalue-ссылку на функцию, а
S2
связывает rvalue-ссылку на функцию:
int f(void(&)()); // перегрузка #1 int f(void(&&)()); // перегрузка #2 void g(); int i1 = f(g); // вызывает #1
S1
и
S2
отличаются только квалификационным преобразованием, и
|
квалификация cv результата
|
(до C++20) |
|
результат
|
(начиная с C++20) |
int f(const int*); int f(int*); int i; int j = f(&i); // &i -> int* лучше чем &i -> const int*, вызывает f(int*)
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 // неоднозначная перегрузка: ошибка компиляции
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
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)
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 преобразуются в массивы одинакового типа элементов, и либо
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) |
Если две последовательности преобразования неразличимы, поскольку они имеют одинаковый ранг, применяются следующие дополнительные правила:
|
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) |
Mid
наследуется (напрямую или косвенно) от
Base
, и
Derived
наследуется (напрямую или косвенно) от
Mid
Derived
в
Mid
&
или
Mid
&&
лучше, чем
Derived
в
Base
&
или
Base
&&
Derived
в
Mid
лучше, чем
Derived
в
Base
Mid
в
Base
&
или
Base
&&
лучше, чем
Derived
в
Base
&
или
Base
&&
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.
|
(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 ! = |
не добавлены |
-
↑
Тип первого параметра встроенного оператора присваивания — «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]