#c #return #language-lawyer #move-semantics #compiler-bug
#c #Возврат #язык-юрист #перемещение-семантика #ошибка компилятора
Вопрос:
Следующий код отклоняется как Clang, так и GCC (магистральные версии):
#include <memory>
struct Base
{
Base() = default;
Base(Base constamp;) = delete;
Base(Baseamp;amp;) = default;
};
struct Derived : Base
{
Derived() = default;
Derived(Derived constamp;) = delete;
Derived(Derivedamp;amp;) = default;
};
auto foo()
-> Base
{
Derived d;
return d; // ERROR HERE
}
Вызывает следующую ошибку:
prog.cc: In function 'Base foo()': prog.cc:21:12: error: use of deleted function 'Base::Base(const Baseamp;)'
return d;
^
Согласно [class.copy]/32:
Когда выполняются критерии исключения операции копирования / перемещения, но не для объявления исключения, а копируемый объект обозначается значением lvalue или когда выражение в операторе return является (возможно, заключенным в скобки) id-выражением, которое называет объект с объявленной автоматической продолжительностью храненияв теле или объявлении параметра самой внутренней функции или лямбда-выражения разрешение перегрузки для выбора конструктора для копии сначала выполняется, как если бы объект был обозначен значением rvalue
Если приведенное выше предложение должно быть проанализировано как (copy elision criteria met amp;amp; lvalue) || (id-expression designating an automatic object)
, как, по-видимому, указывает этот дефект CWG, почему здесь не применяется последнее условие? Есть ли ошибка компилятора как в Clang, так и в GCC?
С другой стороны, если предложение предназначено для анализа как , разве это не очень вводящая в заблуждение формулировка, заслуживающая внимания? (copy elision criteria met amp;amp; (lvalue || id-expression designating an automatic object))
Комментарии:
1. Разве ваш оператор return не соответствует
return Base{d}
? И поэтому должно быть запрещено дизайном? По крайней мере, это то, чего я бы наивно ожидал, не углубляясь в стандарт C .2. @davidhigh: Нарезка не запрещена языком, хотя для нее не так много вариантов использования. Например, вы можете сделать
Derived d; Base b = std::move(d);
. Вопрос здесьd
в том, следует ли рассматривать как значение rvalue (т. Е. Как Если Бы я написалreturn std::move(d)
) или нет.3. спасибо, еще один вопрос: куда следует копировать elision, установленный в? Наивно, он должен соответствовать a
std::move(Base{d})
, а не aBase{std::move(d)}
. Или?4. @davidhigh: Так и должно быть
Base{std::move(d)}
.5. «если тип первого параметра выбранного конструктора не является ссылкой rvalue на тип объекта (возможно, cv-qualified)». Выбранный конструктор принимает
Baseamp;amp;
, нетDerivedamp;amp;
, поэтому результаты первого разрешения перегрузки отбрасываются.
Ответ №1:
[class.copy]/32 продолжается:
[…] если тип первого параметра выбранного конструктора не является ссылкой rvalue на тип объекта (возможно, cv-qualified), разрешение перегрузки выполняется снова, рассматривая объект как lvalue .
Первое разрешение перегрузки, рассматриваемое d
как значение rvalue, выбирает Base::Base(Baseamp;amp;)
. Однако тип первого параметра выбранного конструктора — Derivedamp;amp;
это не но Baseamp;amp;
, поэтому результат этого разрешения перегрузки отбрасывается, и вы снова выполняете разрешение перегрузки, обрабатывая d
как значение lvalue . Это второе разрешение перегрузки выбирает удаленный конструктор копирования.
Комментарии:
1. Ох. Как я это пропустил? В любом случае, это правило является абсолютной, отвратительной мерзостью.
2. Жизнь лучше с сообщениями об ошибках — «main.cpp : В функции ‘Base foo()’: main.cpp:21:12: ошибка: использование удаленной функции ‘Base::Base(const Baseamp;)’ возвращает d; // ОШИБКА ЗДЕСЬ ^ main.cpp:6:5:примечание: объявленный здесь Base(Base constamp;) = delete; ^~~~
3. Кто-нибудь знает происхождение правила, или нам нужно позвонить Ричарду Смиту, чтобы объяснить? 😀
4. Источником является некоторая основная проблема IIRC. Несмотря на это, решение проблемы связанного ядра является неполным, поскольку часть его предназначалась для поддержки показанного кода. Я запишу еще один, как только вернусь.
5. @davidhigh Удаление конструктора копирования приводит к тому, что конструктор по умолчанию и конструктор перемещения не генерируются неявно. Смотрите Эту исправленную версию вашего примера .