16 Overloading [over]

16.3 Overload resolution [over.match]

16.3.1 Candidate functions and argument lists [over.match.funcs]

Подразделы [over.match.funcs] описывают набор функций-кандидатов и список аргументов, представленных для разрешения перегрузки в каждом из семи контекстов, в которых используется разрешение перегрузки. Исходные преобразования и конструкции, определенные в этих подпунктах, предназначены только для описания процесса разрешения перегрузки. Для использования таких преобразований и построений реализация не требуется.

Набор функций-кандидатов может содержать как функции-члены, так и функции, не являющиеся членами, которые должны быть сопоставлены с одним и тем же списком аргументов. Чтобы списки аргументов и параметров были сопоставимы в пределах этого гетерогенного набора, считается, что функция-член имеет дополнительный параметр, называемый implicit object parameter, который представляет объект, для которого была вызвана функция-член. В целях разрешения перегрузки как статические, так и нестатические функции-члены имеют неявный объектный параметр, а конструкторы - нет.

Аналогичным образом, когда это уместно, контекст может создать список аргументов, который содержит символ implied object argument для обозначения объекта, над которым нужно работать. Поскольку аргументы и параметры связаны по позиции в их соответствующих списках, соглашение заключается в том, что неявный параметр объекта, если он присутствует, всегда является первым параметром, а подразумеваемый аргумент объекта, если он присутствует, всегда является первым аргументом.

Для нестатических функций-членов тип неявного параметра объекта -

где X - класс, членом которого является функция, и cv cv-квалификация в объявлении функции-члена. [ Example: Для const функции-члена класса Xпредполагается, что дополнительный параметр имеет тип «ссылка на const X». ] Для функций преобразования функция считается членом класса аргумента неявного объекта с целью определения типа параметра неявного объекта. Для функций без преобразования, введенных a в производный класс, функция считается членом производного класса с целью определения типа неявного параметра объекта. Для статических функций-членов считается, что параметр неявного объекта соответствует любому объекту (поскольку, если функция выбрана, объект отбрасывается). [ Фактический тип не установлен для параметра неявного объекта статической функции-члена, и не будет предпринята попытка определить последовательность преобразования для этого параметра ( ). ]end exampleusing-declarationNote: [over.match.best]end note

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

  • даже если параметр неявного объекта не является константным, значение rvalue может быть привязано к параметру, если во всех других отношениях аргумент может быть преобразован в тип параметра неявного объекта. [ Note: Тот факт, что такой аргумент является rvalue, не влияет на ranking последовательность неявных преобразований. ] end note

Поскольку кроме инициализации списка разрешено только одно определяемое пользователем преобразование в неявной последовательности преобразования, при выборе наилучшего определяемого пользователем преобразования применяются особые правила ([over.match.best], [over.best.ics]). [Example:

class T {
public:
  T();
};

class C : T {
public:
  C(int);
};
T a = 1;            // ill-formed: T(C(1)) not tried

end example]

В каждом случае, когда кандидат является шаблоном функции, специализации шаблона функции кандидата генерируются с использованием вывода аргументов шаблона ([temp.over], [temp.deduct]). Затем эти кандидаты рассматриваются как функции-кандидаты обычным образом.124 Данное имя может относиться к одному или нескольким шаблонам функций, а также к набору перегруженных нешаблонных функций. В таком случае функции-кандидаты, сгенерированные из каждого шаблона функции, объединяются с набором функций-кандидатов, не являющихся шаблоном.

Конструктор перемещения по умолчанию или оператор присваивания ([class.copy]), определенный как удаленный, исключается из набора функций-кандидатов во всех контекстах.

Процесс вывода аргументов полностью определяет типы параметров специализаций шаблонов функций, т. Е. Параметры специализаций шаблонов функций не содержат типов параметров шаблона. Следовательно, если не указано иное, специализации шаблонов функций и нешаблонные functions ([dcl.fct]) обрабатываются одинаково для оставшейся части разрешения перегрузки.

16.3.1.1 Function call syntax [over.match.call]

В function call

postfix-expression ( expression-listopt )

если postfix-expressionобозначает набор перегруженных функций и / или шаблонов функций, применяется разрешение перегрузки, как указано в [over.call.func]. Если postfix-expressionобозначает объект типа класса, применяется разрешение перегрузки, как указано в [over.call.object].

Если postfix-expressionобозначает адрес набора перегруженных функций и / или шаблонов функций, разрешение перегрузки применяется с использованием этого набора, как описано выше. Если функция, выбранная с помощью разрешения перегрузки, является нестатической функцией-членом, программа имеет неправильный формат. [ Note: Разрешение адреса перегрузки, установленного в других контекстах, описано в [over.over]. ] end note

16.3.1.1.1 Call to named function [over.call.func]

Интересны [over.call.func] только те вызовы функций, в которых в postfix-expression конечном итоге содержится имя, обозначающее одну или несколько функций, которые могут быть вызваны. Такой postfix-expression, возможно заключенный произвольно глубоко в круглые скобки, имеет одну из следующих форм:

postfix-expression:
	postfix-expression . id-expression
	postfix-expression -> id-expression
	primary-expression

Они представляют две синтаксические подкатегории вызовов функций: квалифицированные вызовы функций и неквалифицированные вызовы функций.

В квалифицированных вызовах функций разрешаемое имя - это оператор или, id-expression которому предшествует оператор. Поскольку конструкция обычно эквивалентна , остальная часть предложения предполагает без потери общности, что все вызовы функций-членов были нормализованы к форме, которая использует объект и оператор. Кроме того, Clause предполагает, что левый операнд оператора имеет тип « », где обозначает класс . При этом предположении, вызов в вызове рассматривается как функция-член следования правилам поиска имен в classes ( ). Объявления функций, найденные в результате этого поиска, составляют набор функций-кандидатов. Список аргументов в вызове дополнен добавлением левого операнда оператора в вызове нормализованной функции-члена в качестве подразумеваемого аргумента объекта ( ).->.A->B(*A).B [over] . [over] postfix-expression.cv TT125id-expressionT[class.member.lookup]expression-list.[over.match.funcs]

В неквалифицированных вызовах функций имя не уточняется оператором-> или .и имеет более общую форму primary-expression. Имя ищется в контексте вызова функции в соответствии с обычными правилами для name lookup вызовов функций. Объявления функций, найденные в результате этого поиска, составляют набор функций-кандидатов. Из-за правил поиска имени набор функций-кандидатов состоит (1) полностью из функций, не являющихся членами, или (2) полностью из функций-членов некоторого класса T. В случае (1) список аргументов такой же, как expression-list в вызове. В случае (2) список аргументов expression-list в вызове дополнен добавлением подразумеваемого аргумента объекта, как при квалифицированном вызове функции. Если ключевое слово this находится в области видимости и относится к классу Tили производному классу T, то подразумеваемым аргументом объекта является (*this). Если ключевое слово this не входит в область видимости или относится к другому классу, то надуманный объект типа T становится подразумеваемым аргументом объекта126. Если список аргументов дополняется надуманным объектом и при разрешении перегрузки выбирается одна из нестатических функций-членов T, вызов имеет неправильный формат.

Обратите внимание, что cv-квалификаторы для типа объектов важны для разрешения перегрузки как для объектов glvalue, так и для объектов класса prvalue.

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

16.3.1.1.2 Call to object of class type [over.call.object]

Если primary-expression E синтаксис вызова функции оценивается как объект класса типа «cv T», то набор функций-кандидатов включает в себя, по крайней мере, операторы вызова функций T. Операторы вызова функции T получаются обычным поиском имени operator() в контексте (E).operator().

Кроме того, для каждой неявной функции преобразования, объявленной в T форме

operator conversion-type-id () cv-qualifier ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt ;

где cv-qualifier такая же CV-квалификация, что и, или более высокая CV-квалификация, чем,, cvи где conversion-type-id обозначает тип «указатель на функциюP1,,Pnвозврата ( ) R», или тип «ссылка на указатель на функциюP1,,Pnвозврата ( ) R», или введите «ссылка на функциюP1,,Pnвозврата ( ) R», surrogate call function с уникальным именем call-function и имеющей форму

R call-function ( conversion-type-id  F, P1 a1, , Pn an) { return F (a1, , an); }

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

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

Список аргументов, представленный для разрешения перегрузки, состоит из выражений аргументов, присутствующих в синтаксисе вызова функции, которым предшествует подразумеваемый аргумент объекта (E). [ Note: При сравнении вызова с операторами вызова функции подразумеваемый аргумент объекта сравнивается с неявным параметром объекта оператора вызова функции. При сравнении вызова с суррогатной функцией вызова, подразумеваемый аргумент объекта сравнивается с первым параметром суррогатной функции вызова. Функция преобразования, из которой была получена суррогатная функция вызова, будет использоваться в последовательности преобразования для этого параметра, поскольку она преобразует подразумеваемый аргумент объекта в соответствующий указатель функции или ссылку, требуемую этим первым параметром. ] [end noteExample:

int f1(int);
int f2(float);
typedef int (*fp1)(int);
typedef int (*fp2)(float);
struct A {
  operator fp1() { return f1; }
  operator fp2() { return f2; }
} a;
int i = a(1);                   // calls f1 via pointer returned from conversion function

end example]

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

16.3.1.2 Operators in expressions [over.match.oper]

Если ни один из операндов оператора в выражении не имеет типа, который является классом или перечислением, предполагается, что оператор является встроенным оператором и интерпретируется в соответствии с Clause [expr]. [ Note: Поскольку ., .*и ​::​ не могут быть перегружены, эти операторы всегда являются встроенными операторами, интерпретируемыми в соответствии с пунктом [expr]. ?: не может быть перегружен, но правила в этом подпункте используются для определения преобразований, которые будут применяться ко второму и третьему операндам, если они имеют тип класса или перечисления ([expr.cond]). ] [end noteExample:

struct String {
  String (const String&);
  String (const char*);
  operator const char* ();
};
String operator + (const String&, const String&);

void f() {
 const char* p= "one" + "two";  // ill-formed because neither operand has class or enumeration type
 int I = 1 + 1;                 // always evaluates to 2 even if class or enumeration types exist
                                // that would perform the operation.
}

end example]

Если какой-либо из операндов имеет тип, который является классом или перечислением, может быть объявлена ​​пользовательская операторная функция, реализующая этот оператор, или может потребоваться пользовательское преобразование для преобразования операнда в тип, подходящий для встроенного в операторе. В этом случае разрешение перегрузки используется, чтобы определить, какую операторную функцию или встроенный оператор следует вызывать для реализации оператора. Следовательно, обозначение оператора сначала преобразуется в эквивалентное обозначение вызова функции, как показано в таблице 12 (где @ обозначает один из операторов, описанных в указанном подпункте). Однако операнды располагаются в порядке, установленном для встроенного оператора (пункт [expr]).

Таблица 12 - Связь между оператором и обозначением вызова функции
Подпункт Выражение Как функция-член Как функция, не являющаяся членом
[over.unary] @a (a).operator@ () operator@(a)
[over.binary] a@b (a).operator@ (b) operator@(a, b)
[over.ass] a=b (a).operator= (b)
[over.sub] a[b] (a).operator[](b)
[over.ref] a-> (a).operator->()
[over.inc] a@ (a).operator@ (0) operator@(a, 0)

Для унарного оператора @ с операндом типа, чья cv-неквалифицированная версия равна T1, и для бинарного оператора @ с левым операндом типа, чья cv-неквалифицированная версия есть, T1 и правым операндом типа, чья cv-неквалифицированная версия равна T2, три наборы кандидатов функций, обозначены member candidates, non-member candidates и built-in candidates, строятся следующим образом :

  • Если T1 это полный тип класса или класс, определяемый в настоящее время, набор кандидатов в члены является результатом квалифицированного поиска T1​::​operator@ ([over.call.func]); в противном случае набор кандидатов в члены пуст.

  • Набор кандидатов, не являющихся членами, является результатом неквалифицированного поиска operator@ в контексте выражения в соответствии с обычными правилами поиска имени в неквалифицированных вызовах функций ([basic.lookup.argdep]), за исключением того, что все функции-члены игнорируются. Однако, если ни один операнд не имеет типа класса, только те функции, не являющиеся членами в наборе подстановки, которые имеют первый параметр типа T1 или «ссылку на cv T1», когда T1 это тип перечисления или (если есть правый операнд) второй Параметр типа T2 или «ссылка на cv T2», когда T2 является типом перечисления, являются функциями-кандидатами.

  • Для оператора ,, унарного оператора &или оператора ->набор встроенных кандидатов пуст. Для всех других операторов встроенные кандидаты включают в себя все функции операторов-кандидатов, определенные в [over.built] этом, по сравнению с данным оператором,

    • иметь то же имя оператора и

    • принять такое же количество операндов и

    • принимать типы операндов, в которые можно преобразовать данный операнд или операнды в соответствии с [over.best.ics], и

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

Для встроенных операторов присваивания преобразование левого операнда ограничено следующим образом:

  • не вводятся временные файлы для хранения левого операнда, и

  • к левому операнду не применяются определяемые пользователем преобразования для достижения соответствия типа самому левому параметру встроенного кандидата.

Для всех остальных операторов такие ограничения отсутствуют.

Набор функций-кандидатов для разрешения перегрузки - это объединение кандидатов в члены, кандидатов, не являющихся членами, и встроенных кандидатов. Список аргументов содержит все операнды оператора. Лучшая функция из набора функций-кандидатов выбирается в соответствии с [over.match.viable] и [over.match.best].128 [Example:

struct A {
  operator int();
};
A operator+(const A&, const A&);
void m() {
  A a, b;
  a + b;                        // operator+(a, b) chosen over int(a) + int(b)
}

end example]

Если встроенный кандидат выбран путем разрешения перегрузки, операнды типа класса преобразуются в типы соответствующих параметров выбранной функции операции, за исключением того, что вторая стандартная последовательность преобразования a user-defined conversion sequence не применяется. Затем оператор рассматривается как соответствующий встроенный оператор и интерпретируется в соответствии с пунктом [expr]. [Example:

struct X {
  operator double();
};

struct Y {
  operator int*();
};

int *a = Y() + 100.0;           // error: pointer arithmetic requires integral operand
int *b = Y() + X();             // error: pointer arithmetic requires integral operand

end example]

Второй операнд оператора -> игнорируется при выборе operator-> функции и не является аргументом при operator-> вызове функции. При operator-> возврате оператор -> применяется к возвращенному значению с исходным вторым операндом.129

Если оператор является оператором ,, унарным оператором &или оператором ->и нет жизнеспособных функций, то предполагается, что оператор является встроенным оператором и интерпретируется в соответствии с пунктом [expr].

[ Note: Правила поиска для операторов в выражениях отличаются от правил поиска для имен операторных функций в вызове функции, как показано в следующем примере:

struct A { };
void operator + (A, A);

struct B {
  void operator + (B);
  void f ();
};

A a;

void B::f() {
  operator+ (a,a);              // error: global operator hidden by member
  a + a;                        // OK: calls global operator+
}

end note]

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

Если значение, возвращаемое operator-> функцией, имеет тип класса, это может привести к выбору и вызову другой operator-> функции. Процесс повторяется до тех пор, пока operator-> функция не вернет значение неклассового типа.

16.3.1.3 Initialization by constructor [over.match.ctor]

Когда объекты типа класса direct-initializedинициализируются копией из выражения того же или производного типа класса ([dcl.init]), либо при default-initializedразрешении перегрузки выбирается конструктор. Для прямой инициализации или инициализации по умолчанию, которые не находятся в контексте инициализации копирования, все функции-кандидаты являются конструкторами класса инициализируемого объекта. Для инициализации копирования все функции-кандидаты converting constructors принадлежат этому классу. Список аргументов - это expression-listили assignment-expression из initializer.

16.3.1.4 Copy-initialization of class by user-defined conversion [over.match.copy]

В условиях, указанных в [dcl.init], как часть инициализации копии объекта типа класса, может быть вызвано определяемое пользователем преобразование для преобразования выражения инициализатора в тип инициализируемого объекта. Разрешение перегрузки используется для выбора вызываемого пользователем преобразования. [ Note: Преобразование, выполняемое для косвенной привязки к ссылке на тип класса, возможно, квалифицированный cv, определяется в терминах соответствующей инициализации копии без ссылки. ] Предполагая, что « » является типом инициализируемого объекта, с типом класса, функции-кандидаты выбираются следующим образом: end notecv1 TT

  • converting constructors Из T являются функциями кандидатов.

  • Когда тип выражения инициализатора является типом класса «cv S», рассматриваются неявные функции преобразования S и его базовые классы. При инициализации временного объекта, который должен быть привязан к первому параметру конструктора, где параметр имеет тип «ссылка на возможно- cvквалифицированный T», а конструктор вызывается с одним аргументом в контексте прямой инициализации объекта типа «cv2 T» , также рассматриваются явные функции преобразования. Те, которые не скрыты внутри S и дают тип, чья cv-неквалифицированная версия является тем же типом, что T и его производный класс, являются функциями-кандидатами. Функции преобразования, которые возвращают «ссылку на X», возвращают lvalue или xvalue, в зависимости от типа ссылки, типа X и поэтому считаются X подходящими для этого процесса выбора функций-кандидатов.

В обоих случаях список аргументов имеет один аргумент, который является выражением инициализатора. [ Note: Этот аргумент будет сравниваться с первым параметром конструкторов и неявным параметром объекта функций преобразования. ]end note

16.3.1.5 Initialization by conversion function [over.match.conv]

В условиях, указанных в [dcl.init], как часть инициализации объекта неклассового типа, функция преобразования может быть вызвана для преобразования выражения инициализатора типа класса в тип инициализируемого объекта. Разрешение перегрузки используется для выбора вызываемой функции преобразования. Предполагая, что «cv1 T» - это тип инициализируемого объекта, а «cv S» - это тип выражения инициализатора, с S типом класса функции-кандидаты выбираются следующим образом:

  • Рассмотрены функции преобразования S и его базовые классы. Те неявные функции преобразования, которые не скрыты внутри S и дают тип T или тип, который может быть преобразован в тип с T помощью a, standard conversion sequence являются функциями-кандидатами. Для прямой инициализации те явные функции преобразования, которые не скрыты внутри S и дают тип T или тип, который может быть преобразован в тип T с помощью a qualification conversion , также являются функциями-кандидатами. Считается, что функции преобразования, возвращающие тип с квалификацией cv, дают неквалифицированную версию этого типа для этого процесса выбора функций-кандидатов. Функции преобразования, которые возвращают «ссылку на cv2 X», возвращают значения l или x, в зависимости от типа ссылки, типа «cv2 X» и, следовательно, считаются X подходящими для этого процесса выбора функций-кандидатов.

Список аргументов имеет один аргумент, который является выражением инициализатора. [ Note: Этот аргумент будет сравниваться с неявным параметром объекта функций преобразования. ]end note

16.3.1.6 Initialization by conversion function for direct reference binding [over.match.ref]

В условиях, указанных в [dcl.init.ref], ссылка может быть связана непосредственно с glvalue или классом prvalue, который является результатом применения функции преобразования к выражению инициализатора. Разрешение перегрузки используется для выбора вызываемой функции преобразования. Предполагая, что «ссылка на cv1 T» - это тип инициализируемой ссылки, а «cv S» - это тип выражения инициализатора, с S типом класса функции-кандидаты выбираются следующим образом:

  • Рассмотрены функции преобразования S и его базовые классы. Те неявные функции преобразования, которые не скрыты внутри S и дают тип «ссылка lvalue на cv2 T2» (при инициализации ссылки lvalue или ссылки rvalue на функцию) или «cv2 T2» или «ссылка rvalue на cv2 T2» (при инициализации ссылки rvalue или lvalue ссылка на функцию), где «cv1 T» стоит reference-compatible с «cv2 T2», являются функциями-кандидатами. Для прямой инициализации те явные функции преобразования, которые не скрыты внутри S и дают тип «ссылка lvalue на cv2 T2» или «cv2 T2» или «ссылка rvalue на cv2 T2», соответственно, где T2 тот же тип T или может быть преобразован в тип T с a qualification conversion, являются также кандидатские функции.

Список аргументов имеет один аргумент, который является выражением инициализатора. [ Note: Этот аргумент будет сравниваться с неявным параметром объекта функций преобразования. ]end note

16.3.1.7 Initialization by list-initialization [over.match.list]

Когда объекты неагрегированного типа класса T инициализируются списком, что [dcl.init.list] указывает, что разрешение перегрузки выполняется в соответствии с правилами в этом разделе, разрешение перегрузки выбирает конструктор в два этапа:

  • Первоначально функции-кандидаты являются конструкторами списка инициализаторов ([dcl.init.list]) класса, T а список аргументов состоит из списка инициализаторов в виде единственного аргумента.

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

Если список инициализаторов не имеет элементов и T имеет конструктор по умолчанию, первая фаза опускается. При инициализации списка копирования, если explicit выбран конструктор, инициализация имеет неправильный формат . [ Note: Это отличается от других ситуаций ([over.match.ctor], [over.match.copy]), где для инициализации копирования рассматриваются только конструкторы преобразования. Это ограничение применяется только в том случае, если эта инициализация является частью окончательного результата разрешения перегрузки. ] end note

16.3.1.8 Class template argument deduction [over.match.class.deduct]

Формируется набор функций и шаблонов функций, включающий:

  • Для каждого конструктора шаблона первичного класса, обозначенного template-name, если шаблон определен, шаблон функции со следующими свойствами:

    • Параметры шаблона - это параметры шаблона шаблона класса, за которыми следуют параметры шаблона (включая аргументы шаблона по умолчанию) конструктора, если таковые имеются.

    • Типы параметров функции такие же, как у конструктора.

    • Тип возвращаемого значения - это специализация шаблона класса, обозначенная template-name аргументами и шаблона, соответствующими параметрам шаблона, полученным из шаблона класса.

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

  • Дополнительный шаблон функции, полученный, как указано выше, из гипотетического конструктора C(C), который называется copy deduction candidate.

  • Для каждого из них deduction-guide- шаблон функции или функции со следующими свойствами:

Инициализация и разрешение перегрузки выполняются, как описано в [dcl.init] и [over.match.ctor],, [over.match.copy]или [over.match.list] (в зависимости от типа выполняемой инициализации) для объекта гипотетического типа класса, где выбранные функции и шаблоны функций считаются конструкторами этого типа класса для цель формирования набора перегрузки, а инициализатор предоставляется контекстом, в котором было выполнено вывод аргументов шаблона класса. Каждый такой условный конструктор считается явным, если функция или шаблон функции были сгенерированы из конструктора или deduction-guideбыли объявлены explicit. Все такие условные конструкторы считаются открытыми членами гипотетического типа класса.

[Example:

template <class T> struct A {
  explicit A(const T&, ...) noexcept;  // #1
  A(T&&, ...);                         // #2
};

int i;
A a1 = { i, i };    // error: explicit constructor #1 selected in copy-list-initialization during deduction,
                    // cannot deduce from non-forwarding rvalue reference in #2

A a2{i, i};         // OK, #1 deduces to A<int> and also initializes
A a3{0, i};         // OK, #2 deduces to A<int> and also initializes
A a4 = {0, i};      // OK, #2 deduces to A<int> and also initializes

template <class T> A(const T&, const T&) -> A<T&>;  // #3
template <class T> explicit A(T&&, T&&) -> A<T>;    // #4

A a5 = {0, 1};      // error: explicit deduction guide #4 selected in copy-list-initialization during deduction
A a6{0,1};          // OK, #4 deduces to A<int> and #2 initializes
A a7 = {0, i};      // error: #3 deduces to A<int&>, #1 and #2 declare same constructor
A a8{0,i};          // error: #3 deduces to A<int&>, #1 and #2 declare same constructor

template <class T> struct B {
  template <class U> using TA = T;
  template <class U> B(U, TA<U>);
};

B b{(int*)0, (char*)0};         // OK, deduces B<char*>

end example]