clang++ терпит неудачу, но g ++ успешно использует приведение к оператору const-unrelated-type в присваивании

Вот короткий пример, который воспроизводит это "нежизнеспособное преобразование" с лимоном для clang, но справедливо для g ++ различий в поведении компилятора.

#include <iostream>

struct A { 
    int i; 
};

#ifndef UNSCREW_CLANG
using cast_type = const A;
#else 
using cast_type = A;
#endif

struct B {
    operator cast_type () const {
        return A{i};
    }
    int i;
}; 

int main () { 
    A a{0};
    B b{1};

#ifndef CLANG_WORKAROUND
    a = b;
#else    
    a = b.operator cast_type ();
#endif    

    std::cout << a.i << std::endl;    

    return EXIT_SUCCESS;
}

жить у Годболта

g++ (4.9, 5.2) компилирует это молча; тогда как clang++ (3.5, 3.7) компилирует его

если

using cast_type = A;

или же

using cast_type = const A;
// [...] 
a = b.operator cast_type ();

используются, но не с дефолтом

using cast_type = const A;
// [...] 
a = b; 

В этом случае обвиняет clang++ (3.5) a = b:

testling.c++:25:9: error: no viable conversion from 'B' to 'A'
    a = b;
        ^
testling.c++:3:8: note: candidate constructor (the implicit copy constructor) 
not viable:
      no known conversion from 'B' to 'const A &' for 1st argument
struct A { 
       ^
testling.c++:3:8: note: candidate constructor (the implicit move constructor) 
not viable:
      no known conversion from 'B' to 'A &&' for 1st argument
struct A { 
       ^
testling.c++:14:5: note: candidate function
    operator cast_type () const {
    ^
testling.c++:3:8: note: passing argument to parameter here
struct A { 

Со ссылкой на стандарт 2011¹: правильно ли clang ++ отклонять код по умолчанию или g ++ правильно его принимать?

Примечание: вопрос не в том, const классификатор на cast_type имеет смысл. Речь идет о том, какой компилятор работает в соответствии со стандартами, и только об этом.

¹ 2014 год не должен иметь значения здесь.

РЕДАКТИРОВАТЬ:

Пожалуйста, воздержитесь от повторной пометки этого тегом C++. Сначала я хотел бы узнать, какое поведение соответствует стандарту 2011 года, и сохранить приверженность комитетов. not to break existing (< 2011) code из анзаца пока.

1 ответ

Решение

Похоже, что это покрыто этим сообщением об ошибке clang. Перегрузка по значению скрывает const lvalue? который имеет следующий пример:

struct A{};
struct B{operator const A()const;};
void f(A const&);
#ifdef ERR
void f(A&&);
#endif
int main(){
  B a;
  f(a);
}

которая завершается с той же ошибкой, что и код OP. Ричард Смит под конец говорит:

Обновление: мы правильно выбрали 'f(A&&)', но мы ошибаемся, чтобы отклонить инициализацию параметра. Далее уменьшается:

  struct A {};
  struct B { operator const A(); } b;
  A &&a = b;

Здесь [dcl.init.ref]p5 bullet 2 bullet 1 bullet 2 не применяется, потому что [over.match.ref]p1 не находит подходящих функций преобразования, потому что "A" не совместим со ссылками с "const A". Таким образом, мы попадаем в [dcl.init.ref]p5 bullet 2 bullet 2, копируем-инициализируем временный тип A из 'b' и привязываем ссылку на него. Я не уверен, где в этом процессе мы пойдем не так.

но затем возвращается с другим комментарием из-за сообщения об ошибке 1604:

DR1604 изменил правила так, чтобы

 A &&a = b;

сейчас плохо сформирован. Так что теперь мы правы, чтобы отклонить инициализацию. Но это все еще ужасный ответ; Я снова подтолкнул CWG. Вероятно, нам следует отказаться от f (A &&) во время разрешения перегрузки.

Таким образом, кажется, что технически Clang делает правильные вещи на основе стандартного языка сегодня, но это может измениться, так как, по-видимому, есть несогласие, по крайней мере, с командой Clang, что это правильный результат. Предположительно, это приведет к появлению отчета о дефектах, и нам придется подождать, пока он будет решен, прежде чем мы сможем прийти к окончательному выводу.

Обновить

Похоже, дефектный отчет 2077 был подан на основании этой проблемы.

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