Учитывая следующие операторы преобразования
struct A
{
template<typename T> explicit operator T&& () &&;
template<typename T> explicit operator T& () &;
template<typename T> explicit operator const T& () const&;
};
struct B {};
Я ожидаю, что следующие преобразования будут действительными, но некоторые из них дают ошибки компиляции (живой пример):
A a;
A&& ar = std::move(a);
A& al = a;
const A& ac = a;
B&& bm(std::move(a)); // 1. OK
B&& bt(A{}); // 2. OK
B&& br(ar); // 3. error: no viable conversion from A to B
B& bl(al); // 4. OK
const B& bz(al); // 5. OK
const B& bc(ac); // 6. OK
B cm(std::move(a)); // 7. error: call to constructor of B ambiguous
B ct(A{}); // 8. error: call to constructor of B ambiguous
B cr(ar); // 9. OK
В частности, 1, кажется, идентичен 3 и почти идентичен 2 (аналогично для 7-9, 8), но ведет себя по-разному.
Любое объяснение или обходной путь?
Моя мотивация Еще один «любой», где мне в итоге пришлось сделать все операторы преобразования explicit
чтобы избежать проблем с чертами типа std::is_constructible
, std::is_convertible
Тогда я столкнулся с новыми проблемами.
РЕДАКТИРОВАТЬ Извините, пожалуйста, игнорируйте 3 и 9, моя ошибка (спасибо Kerrek SB). Тем не менее, 7 и 8 остаются проблемами. Также, explicit
кажется, в конце концов не имеет значения, еще раз извините.
РЕДАКТИРОВАТЬ 2 Просто заметил что
B cm = std::move(a);
B ct = A{};
действительны, если операторы преобразования не explicit
, Так вот где explicit
приходит: первоначально мои образцы использовали инициализацию копирования, а когда я переключился на explicit
Мне пришлось использовать прямую инициализацию. затем эта проблема возникла (случаи 7 и 8).
Все же 7 и 8 остаются как проблемы
B cm(std::move(a)); // 7. error: call to constructor of B ambiguous
B ct(A{}); // 8. error: call to constructor of B ambiguous
Два случая одинаковы: прямая инициализация с аргументом rvalue типа A.
Все функции-кандидаты для прямой инициализации являются конструкторами, и в этом случае оба являются конструктором копирования. B::B(const B&)
и переместить конструктор B(B&&)
жизнеспособны, так как существует неявное преобразование из значения А в оба const B&
и к B&&
, Разрешение перегрузки не может определиться между этими двумя конструкторами, потому что для вызова любого из них требуется пользовательское преобразование непосредственно в тип параметра, а пользовательские последовательности преобразования ранжируются только второе стандартное преобразование:
13.3.3.2/3[over.ics.rank]
Пользовательская последовательность преобразования U1 является лучшей последовательностью преобразования, чем другая пользовательская последовательность преобразования U2, если они содержат одну и ту же пользовательскую функцию преобразования … и вторая стандартная последовательность преобразования U1 лучше, чем вторая стандартная последовательность преобразования U2 «.
Это отличается от вызова функции-члена, которая имеет && и конст &перегрузки, потому что в этом случае разрешение перегрузки ранжирует привязки ссылок из аргумента rvalue, чтобы повлиять на параметр объекта в соответствии с
Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если S1 и S2 являются ссылочными привязками (8.5.3) и ни одна из них не ссылается на неявный параметр объекта нестатической функции-члена, объявленной без квалификатора ref, и S1 привязывает ссылку rvalue к значению rvalue, а S2 привязывает ссылку lvalue.