Приоритет в выборе функций преобразования для присвоения инициализации
Рассмотрим следующий фрагмент кода:
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 - нет (почему?).
Итак, вопрос:
- Почему оператор преобразования всегда выбирается в этом случае?
- Почему оба cv-qualified вызывают двусмысленность, а оба cv-unqualified - нет?
1 ответ
Для разрешения перегрузки существует неявный параметр объекта для A::operator B()
чей тип cv A&
, Этот параметр является особенным, так как он может быть связан с rvalue, даже если это ссылка lvalue на неконстантный тип в соответствии с [over.match.funcs] / 5:
Во время разрешения перегрузки подразумеваемый объектный аргумент неотличим от других аргументов. Однако неявный параметр объекта сохраняет свою идентичность, так как никакие пользовательские преобразования не могут быть применены для достижения соответствия типа с ним. Для нестатических функций-членов, объявленных без квалификатора ref, применяется дополнительное правило:
- даже если параметр неявного объекта не является константно-квалифицированным, значение r может быть связано с параметром, если во всех других отношениях аргумент может быть преобразован в тип параметра неявного объекта. [Примечание: тот факт, что такой аргумент является r-значением, не влияет на ранжирование неявных последовательностей преобразования. - конец примечания]
За исключением неявного параметра объекта, для которого см. [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, между ними нет различий, и не применяется никакого специального правила, поэтому разрешение перегрузки не удается из-за неоднозначности.