Преобразование в `const Y` не применимо для` R & amp; `на clang

Следующий код прекрасно компилируется с g++ (GCC) 4.7.1 20120721, но
терпит неудачу с недавней сборкой clang version 3.2 (trunk),

struct Y {};

struct X {
operator const Y() const { return Y(); }
};

void f(Y&& y) {}

int main()
{
f(X());
return 0;
}

Изменение оператора преобразования на operator Y() const достаточно
чтобы код компилировался на обоих компиляторах.

Какой компилятор на самом деле соответствует стандарту в этом случае? Что значит
на самом деле стандарт говорит об этом?

Дословная ошибка в соответствии с просьбой:

bla.cpp:14:5: error: no viable conversion from 'X' to 'Y'
f(X());
^~~
bla.cpp:1:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'X' to
'const Y &' for 1st argument
struct Y {
^
bla.cpp:1:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'X' to
'Y &&' for 1st argument
struct Y {
^
bla.cpp:6:3: note: candidate function
operator const Y() const { return Y(); }
^
bla.cpp:10:12: note: passing argument to parameter 'y' here
void f(Y&& y) {}
^

РЕДАКТИРОВАТЬ: К сожалению даже добавление перегрузки

void f(const Y&) {}

по-прежнему заставляет Clang выбрать эталонную перегрузку rvalue, и поэтому
ломает существующий код, который использовался для компиляции, например, со стандартным
контейнеры.

5

Решение

Я считаю, что Clang прав, чтобы отвергнуть это. Передача аргумента f(Y&&) требует двух шагов преобразования, первый из которых ваш operator const Y() а второе существо YКопируй конструктор. Я считаю, что оба они считаются пользовательскими преобразованиями, и оба являются неявными, что нарушает принцип, согласно которому последовательность неявных преобразований включает только одно пользовательское преобразование.

это Цель возврата по постоянному значению? содержит некоторые интересные идеи о семантике возвращения const T,

Хм, если я попытаюсь добавить перегрузку void f(const Y&y) как сейчас отредактированный вопрос, лязг ведет себя довольно странно. Он все еще жалуется на невозможность конвертировать X в Yи даже не перечисляет перегрузку f(const Y& y) в его диагностике. Но как только я меняю перегрузку на Y по значению, то есть написать void f(const Y y)жалуется на звонок f быть неоднозначным

Это с лязг XCode 4.5, который сообщает Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn), Если вы можете воспроизвести это с помощью ванильного лязга, вы, вероятно, должны сообщить об этом в списке рассылки clang — наверняка есть ошибка, скрывающаяся там где-то

6

Другие решения

Пример плохо сформирован.

Некоторые цитаты N3242.

8.5.3 пункт 4:

Данные типы »CV1 T1» а также «CV2 T2, «»CV1 T1«Связан со ссылкой на»CV2 T2» если T1 тот же тип, что и T2, или же T1 это базовый класс T2, «CV1 T1«Является справочно-совместимым с»CV2 T2» если T1 имеет отношение к T2 а также CV1 является той же квалификацией cv или большей квалификацией, чем CV2.

пункт 5 (метки от пуль мои):

Ссылка на тип «CV1 T1«инициализируется выражением типа»CV2 T2» следующее:

  1. Если ссылка является ссылкой lvalue и …
  2. В противном случае ссылка должна быть ссылкой lvalue на энергонезависимый тип const (т.е. CV1 должен быть const), или ссылка должна быть ссылкой.

    а. Если выражение инициализатора

    • я. это xvalue, класс prvalue, массив prvalue или функция lvalue и «CV1 T1«является справочно-совместимым с»CV2 T2«, или же
    • II. имеет тип класса (т.е. T2 это тип класса), где T1 не имеет отношения к T2и может быть неявно преобразовано в xvalue, класс prvalue или функцию lvalue типа «CV3 T3«, где «CV1 T1«является справочно-совместимым с»CV3 T3»,
    • тогда ссылка связана с ….

    б. В противном случае, временный тип «CV1 T1«создается и инициализируется из выражения инициализатора с использованием правил неинициализированной копии-инициализации (8.5). Ссылка затем привязывается к временной. T1 имеет отношение к T2, CV1 должна быть той же квалификации, что и квалификация, или большей, чем CV2. …

Для инициализации параметра функции, T1 является Y, T2 является X, а также CV1 а также CV2 оба пусты. 1 отсутствует: ссылка является rvalue ссылкой, а не lvalue ссылкой. 2.a.i. снаружи: X не совместим с Y, 2.b. из-за копирования инициализации Y от значения типа X включает в себя два пользовательских преобразования: функция преобразования, а затем конструктор копирования Y, (Точный запрет в 13.3.3.1p4.)

Для случая 2.a.ii. очевидный выбор дляCV3 T3» является const Y, но это не хорошо, потому что Y не совместим с const Y, Вы можете поспорить за попытку T3 является Y а также CV3 пусто, но тогда вы вернулись к необходимости конструктора копирования Y в качестве второго неявного пользовательского преобразования.

2

По вопросам рекламы [email protected]