Привязка ссылок через оператор неоднозначного преобразования

#include <iostream>
using namespace std;

struct CL2
{
    CL2(){}
    CL2(const CL2&){}
};

CL2 cl2;

struct CL1
{
    CL1(){}
    operator CL2&(){cout<<"operator CL2&"; return cl2;}
    operator const CL2&(){cout<<"operator const CL2&"; return cl2;}
};

CL1 cl1;

int main()
{
    CL1 cl1;
    CL2 cl2 (cl1);
}

И clang, и gcc предоставляют неоднозначный оператор преобразования, но Visual Studio компилирует ok и печатает "operator const CL2&". Как должно быть правильно в соответствии со стандартом?
Как я понимаю, преобразование CL1 в const CL2 & находится в контексте инициализации копирования (как часть прямой инициализации объекта cl2). Я видел черновик n4296, [over.match.copy]:

Предполагая, что "cv1 T" является типом инициализируемого объекта, а T - тип класса, функции-кандидаты выбираются следующим образом:
- Конвертирующие конструкторы (12.3.1) из T являются функциями-кандидатами.
- Когда типом выражения инициализатора является тип класса "cv S", рассматриваются неявные функции преобразования S и его базовых классов. При инициализации временного объекта для привязки к первому параметру конструктора, где параметр имеет тип "ссылка на, возможно, c-квалифицированную T", и конструктор вызывается с одним аргументом в контексте прямой инициализации объекта типа "Cv2 T", явные функции преобразования также рассматриваются. Те, которые не скрыты в S и дают тип, чья cv-неквалифицированная версия имеет тот же тип, что и T, или является ее производным классом, являются функциями-кандидатами. Функции преобразования, которые возвращают "ссылку на X", возвращают lvalue или xvalues, в зависимости от типа ссылки, типа X и, следовательно, считаются выдающими X для этого процесса выбора функций-кандидатов.

Т.е. оба оператора преобразования считаются возвращаемыми CL2 и const CL2 (а не просто CL2 без const), и остается решить, какое преобразование лучше: CL2 -> const CL2& или const CL2 -> const CL2&. Второй случай кажется более подходящим. Должно ли в этом контексте рассматриваться улучшение конверсии квалификации? Или оба случая являются преобразованием идентичности? Я не смог найти его в стандарте

1 ответ

Решение

Поскольку оба оператора преобразования имеют одинаковые подписи, единственным способом, которым один из них может быть предпочтительнее другого, является применение [over.match.best]/(1.4)…

- контекст является инициализацией путем пользовательского преобразования (см. 8.5, 13.3.1.5 и 13.3.1.6) и стандартной последовательности преобразования из возвращаемого типа F1 для типа назначения (т. е. тип инициализируемого объекта) является лучшей последовательностью преобразования, чем стандартная последовательность преобразования из возвращаемого типа F2 к типу назначения.

… Или (1.5):

- контекст является инициализацией функцией преобразования для прямой привязки ссылки (13.3.1.6) ссылки на тип функции, […]

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

operator CL2&();
operator const CL2&() const; 

Демо; Здесь исходная стандартная последовательность преобразования предыдущей перегрузки для неявного аргумента объекта лучше согласно [over.ics.rank]/(3.2.6), что является решающим для [over.match.best]/(1.3).

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