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

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

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 -qualifiers, и тип, к которому относится ссылка, инициализированная с помощью S2, является более квалифицированным по cv, чем тип, к которому относится ссылка, инициализированная с помощью S1.

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

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

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

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 (отличный от неявного параметра объекта), а соответствующий аргумент потребует создания временного объекта для инициализации Ссылка на значение (см. [dcl.init.ref]). - конец примечания]

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


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

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

Тогда в соответствии с [over.ics.rank] / 3:

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

  • ...

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

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


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

Другие вопросы по тегам