Рассмотрим следующий код:
#include <iostream>
struct Thing
{
Thing(void) {std::cout << __PRETTY_FUNCTION__ << std::endl;}
Thing(Thing const &) = delete;
Thing(Thing &&) = delete;
Thing & operator =(Thing const &) = delete;
Thing & operator =(Thing &&) = delete;
};
int main()
{
Thing thing{Thing{}};
}
Я ожидаю, что оператор Thing thing{Thing{}}; будет означать создание временного объекта класса Thing с использованием конструктора по умолчанию и создание объекта thing класса Thing с использованием конструктора перемещения с только что созданным временным объектом в качестве аргумента. И я ожидаю, что эта программа будет считаться плохо сформированной, потому что она содержит вызов конструктора удаленного перемещения, даже если он потенциально может быть опущен. Раздел стандарта class.copy.elision, похоже, тоже требует этого:
выбранный конструктор должен быть доступен, даже если вызов отклонен
Формулировка гарантированного исключения копий с помощью упрощенных категорий значений / a>, похоже, тоже этого не допускает.
Однако gcc 7.2 (и clang 4 тоже, но не VS2017, который все еще не поддерживает гарантированное исключение копирования) будет просто скомпилировать этот код, исключив перемещение вызов конструктора.
Какое поведение было бы правильным в этом случае?
Ответ 1 · рейтинг 11 · StoryTeller - Unslander Monica
Он не создает плохо сформированную программную сборку. Он полностью избавляется от ссылки на удаленную функцию. Соответствующая формулировка предложения находится здесь:
[dcl.init] пуля 17.6
Если выражение инициализатора является prvalue, а версия cv-unqualified исходного типа является тем же классом, что и класс назначения, выражение инициализатора используется для инициализации объекта назначения. [Пример: Т х = Т (Т (Т ())); вызывает конструктор по умолчанию T для инициализации x. ]
Пример еще больше усиливает это. Поскольку он указывает, что все выражение должно свернуться в единую конструкцию по умолчанию.
Следует отметить, что удаленная функция никогда не используется odr, когда копии исключаются из-за категорий значений, поэтому программа не ссылается на нее.
Это важное различие, поскольку другая форма исключения копирования по-прежнему odr-использует c'tor копирования, как описано здесь:
[basic.def.odr] / 3
... Конструктор, выбранный для копирования или перемещения объекта типа класса, используется odr, даже если вызов фактически отменен реализацией ([class.copy] ...
[class.copy] описывает другую форму допустимого (но не обязательного) исключения из копирования. Что, если мы продемонстрируем с вашим классом:
Thing foo() {
Thing t;
return t; // Can be elided according to [class.copy.elision] still odr-used
}
Должен сделать программу некорректной. И GCC жалуется на это, как и ожидалось.
И кстати. Если вы думаете, что предыдущий пример в онлайн-компиляторе - это фокус фокусников, а GCC жалуется, потому что ему нужно вызвать c'tor перемещения. Посмотрите, что происходит, когда мы даем определение.