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

Рассмотрим следующий фрагмент кода:

class A;

class B { 
      public: 
         B(){} 

         B(A&) // conversion constructor that takes cv-unqualified A
         { 
              cout << "called B's conversion constructor" << endl; 
         } 
};

class A { 
      public: 
         operator B() const // conversion operator that takes cv-qualified A
         { 
              cout << "called A's conversion operator" << endl; 
              return B(); 
         } 
};

int main()
{
    B b = A(); // who gets called here?
    return 0;
}

Согласно этому вопросу, выигрывает последовательность преобразования с наименьшей формой, соответствующей cv (13.3.3.2/3 в спецификации):

Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если [...] S1 и S2 являются привязками ссылок (8.5.3) и типы, на которые ссылаются ссылки, одинаковы. тип, за исключением cv-квалификаторов верхнего уровня, и тип, на который ссылается ссылка, инициализированная S2, является более cv-квалифицированным, чем тип, на который ссылается ссылка. инициализируется S1, ссылается.

Тем не менее, в приведенном выше фрагменте всегда выбирается оператор преобразования, независимо от того, является ли A cv-квалифицированным или нет в обеих функциях. Единственным исключением является то, что когда и конструктор, и оператор имеют квалификацию cv, компилятор жалуется на двусмысленность в выборе последовательности преобразования, в то время как в случае cv-unqualified нет (почему?).

Итак, вопрос:

  1. Почему в этом случае всегда выбирается оператор преобразования?
  2. Почему оба cv-qualified вызывают двусмысленность, а оба cv-unqualified нет?

person Johnson Steward    schedule 16.03.2018    source источник
comment
В лучшем случае может быть вызвано только одно неявное определяемое пользователем преобразование.   -  person    schedule 16.03.2018


Ответы (1)


Для разрешения перегрузки существует неявный объектный параметр для A::operator B(), тип которого cv A&. Этот параметр уникален тем, что его можно привязать к rvalue, даже если он является ссылкой lvalue на неконстантный тип в соответствии с [over.match.funcs]/5:

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

  • даже если неявный объектный параметр не является константным, значение r может быть привязано к параметру, если во всех других отношениях аргумент может быть преобразован в тип неявного объектного параметра. [ Примечание. Тот факт, что такой аргумент является значением r, не влияет на ранжирование последовательностей неявного преобразования. — конец примечания ]

и [over.ics.ref]/3

За исключением неявного параметра объекта, для которого см. [over.match.funcs], стандартная последовательность преобразования не может быть сформирована, если она требует привязки ссылки lvalue, отличной от ссылки на неизменяемый тип const, к rvalue или привязки rvalue ссылка на lvalue, отличную от функции lvalue. [ Примечание. Это означает, например, что функция-кандидат не может быть жизнеспособной функцией, если она имеет неконстантный ссылочный параметр lvalue (отличный от неявного параметра объекта) и соответствующий аргумент потребует создания временного объекта для инициализации ссылка на lvalue (см. [dcl.init.ref]). — конец примечания ]

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


Если конструктор преобразования принимает константный параметр, а оператор преобразования не является константным, оба неявных преобразования являются преобразованиями идентификаторов в соответствии с [over.ics.ref]/1:

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

Затем согласно [over.ics.rank]/3< /а>:

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

  • ...

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

Поэтому выбрана неконстантная версия (оператор преобразования).


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

person xskxzr    schedule 16.03.2018