Какова степень неявного преобразования для инициализации копирования списка - PullRequest
0 голосов
/ 26 февраля 2020
#include <iostream>
struct A{
  A(int){

  }
};
struct B{
  B() = default;
  B(A){

  }
  B(B const&){}
  B(B&&){}
};

int main(){
  B b({0});
}

Для данных кодов возможными функциями являются:

 #1  B::B(A)   
 #2  B::B(const B&)  
 #3  B::B(B&&)  

В соответствии со стандартом, для # 1 объект типа A инициализируется в виде списка копирования {0} как A a = {0}, A::A(int) рассматривается для инициализации, поэтому только стандартное преобразование в # 1. Для # 2 это инициализация ссылочной формы braced-init-list, которая является причиной [dcl.init.list]

В противном случае, если T является ссылочным типом, тип значения, на который ссылается T, генерируется. Значение prvalue инициализирует свой объект результата путем инициализации copy-list-initialization или direct-list-initialization, в зависимости от вида инициализации для ссылки. Затем значение prvalue используется для прямой инициализации ссылки. [Примечание: Как обычно, привязка завершится неудачно, и программа будет некорректной, если ссылочный тип является lvalue-ссылкой на неконстантный тип. - примечание конца]

Таким образом, оно приравнивается к const B& = {0}, в этой инициализации функция преобразования равна B::B(A), а аргумент равен 0, поэтому B tmp = {0} и 'B :: B (A) 'считается, что параметр инициализируется аргументом 0, как A parameter = 0.

В противном случае (т. Е. Для остальных случаев инициализации копирования), определяется пользователем последовательности преобразования, которые могут преобразовывать тип источника в тип назначения или (если используется функция преобразования) в его производный класс, перечисляются, как описано в [over.match.copy], и лучший из них выбирается с помощью перегрузки. разрешение ...

Таким образом, в # 2 существует пользовательское преобразование, и ситуация # 3 такая же, как в # 2, и соответствует [over.ics.rank ] ,

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

стандартное преобразование лучше чем определяемое пользователем преобразование, поэтому # 1 должно быть лучше, чем # 2 и # 3, но на самом деле, g ++ сообщает, что вызов неоднозначен, почему? Сообщение об ошибке:

main.cpp: In function ‘int main()’:
main.cpp:12:10: error: call of overloaded ‘B(<brace-enclosed initializer list>)’ is ambiguous
   B b({0});
          ^
main.cpp:8:3: note: candidate: B::B(A)
   B(A){
   ^
main.cpp:6:8: note: candidate: constexpr B::B(const B&)
 struct B{
        ^
main.cpp:6:8: note: candidate: constexpr B::B(B&&)

Ответы [ 2 ]

0 голосов
/ 20 апреля 2020

Ответы здесь, поскольку аргумент список инициализаторов , поэтому правила [over.ics.list] выполняются при разрешении перегрузки.

[over.ics.list] / 6

В противном случае, если параметр является неагрегированным классом X и разрешение перегрузки для [over.match.list] выбирает единственный лучший конструктор C из X для выполнения инициализации объекта введите X из списка инициализаторов аргументов:
  • Если C не является конструктором списка инициализаторов, а список инициализаторов содержит один элемент типа cv U, где U - X или класс, производный от X, последовательность неявного преобразования имеет ранг точного соответствия, если U равен X, или ранг преобразования, если U получена из X.
  • В противном случае последовательность неявного преобразования представляет собой определяемую пользователем последовательность преобразования со вторым стандартная последовательность преобразования преобразования личности.

Следовательно, для этих трех кандидатов в конструкторы все их параметры относятся к классу неагрегирующих классов, поэтому для преобразования одного элемента используется неявная последовательность преобразования. списка инициализатора для соответствующего типа параметра конструктора параметра конструктора B, и, следовательно, все эти последовательности преобразования являются определяемой пользователем последовательностью преобразования , а вторая стандартная последовательность преобразования является единичными преобразованиями. из них неотличимы .

0 голосов
/ 26 февраля 2020

Все три преобразования {0} -> A, {0} -> const B&, {0} -> B&& являются пользовательскими преобразованиями.

Для преобразования {0} в A, происходит другое разрешение перегрузки , и на этот раз вы столкнетесь с тремя конструкторами A(int), A(const A&) и A(A&&). Поскольку 0 -> int является стандартным преобразованием, в то время как 0 -> const A& и 0 -> A&& являются определяемыми пользователем преобразованиями, преобразование 0 -> int выигрывает и A(int) выбирается для преобразования {0} в A.

Ваша путаница возникает из-за смешивания двух разрешений перегрузки.

...