Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Разрешено ли копирование / перемещение elision, чтобы программа, использующая удаленные функции, имела правильный формат?

Автор вопроса: user7860670Ответов: 1

Рассмотрим следующий код:

#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, который все еще не поддерживает гарантированное исключение копирования) будет просто скомпилировать этот код, исключив перемещение вызов конструктора.

Какое поведение было бы правильным в этом случае?

  • Почему вы ожидаете временное значение здесь, а не в Thing thing{f()};, где f() возвращает Thing? Bo Persson 06.09.2017
  • Кроме того, я подозреваю, что доступный - значит не частный. Bo Persson 06.09.2017
  • @BoPersson Нет, он также работает с частными конструкторами. Hatted Rooster 06.09.2017
  • @BoPersson Я ожидал, что Thing thing{f()}; будет означать вызов конструктора перемещения с временным, возвращаемым f() вызовом аргумента. Мы надеемся, что этого удастся избежать накладных расходов на создание временных файлов. user7860670 06.09.2017
  • Формулировка для гарантированного исключения копий с помощью упрощенных категорий значений, похоже, тоже не позволяет этого Да, но на удивление мало людей понимают как (или не торопятся отвечать) . Выбранный ответ показывает только пример, но не является нормативной формулировкой. И нормативная формулировка перед Примером не гарантирует, что весь T(T(T())) сворачивается в () или что конструктор копирования / перемещения не выбран при разрешении перегрузки. Language Lawyer 18.06.2020
  • Кстати, эта формулировка, похоже, даже не применима к вашему делу. Language Lawyer 18.06.2020

c++constructorc++17copy-elision

Ответы

Ответ 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 перемещения. Посмотрите, что происходит, когда мы даем определение.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы