Вопрос: это "правильное" поведение? Я думал, что нет особого приоритета между конструктором преобразования и оператором преобразования [...]
Это не совсем верно. Вы смотрите на код как есть:
struct Y {
Y() {}
Y(X&) = delete;
};
Но на самом деле там есть нечто большее. Для компилятора Y
выглядит так:
struct Y {
Y() {}
Y(X&) = delete;
Y(Y&&) = default;
Y(Y const&) = default;
};
Выбор здесь не между Y(X&)
и X::operator Y()
. Выбор в основном между Y(X&)
и Y(Y&&)
. И первое совпадение лучше, чем второе (независимо от того, как вы упомянули в вопросе, это X&
или X const&
в качестве параметра). Но он удален, поэтому преобразование является неправильным.
<ч />
Если бы мы инициализировали копию вместо прямой инициализации:
Y y = x;
Тогда оба будут одинаково жизнеспособными (и, следовательно, неоднозначными). И да, вы действительно хотите, чтобы это было неоднозначным. = delete
не удаляет из набора кандидатов!
При изменении конструктора с Y(X&)
на Y(X const&)
предпочтительна функция преобразования.
<ч />
Да, это можно назвать глупым, но на самом деле есть встроенные типы, которые ведут себя так: заменитель X <- int&
, Y <- int&&
Да, но в исходном примере X
и Y
отличаются типами . Здесь они представляют разные категории значений одного и того же типа. Причина, по которой этот новый пример работает или не работает, совершенно иная:
X x;
Y y1(x); // Error
Y y2 = static_cast<Y>(x); // OK
действительно:
int& x = ...;
int&& y(x); // error, can't bind rvalue reference to lvalue
int&& y2 = static_cast<int&&>(x); // ok. this is exactly std::move(x)
Ссылочная привязка к ссылочно-совместимым типам - не тот же вопрос, что и приоритет преобразования.