Подтвердить что ты не робот

Почему конструктор перемещения не объявлен и не удален с помощью clang?

Рассмотрим следующие классы.

struct with_copy {
    with_copy() = default;
    with_copy(with_copy const&) {}
    with_copy& operator=(with_copy const&) { return *this; }
};

struct foo {
    with_copy c;
    std::unique_ptr<int> p;
};
  • Имеет ли with_copy конструктор копирования? Да. Он был явно определен.
  • Есть ли with_copy конструктор перемещения? Нет. Явный конструктор копирования предотвращает его создание.
  • Имеет ли with_copy конструктор удаленных перемещений? Нет. Отсутствие конструктора перемещения не совпадает с удаленным. Конструктор с удаленным ходом попытается переместить плохо сформированный, а не дегенерировать на копию.
  • Скопирован ли with_copy? Да. Его копия используется для копирования.
  • Является ли with_copy подвижным? Да. Его конструктор копирования используется для перемещений.

... и теперь сложные.

  • Имеет ли foo конструктор копирования? Да. Он имеет удаленный, поскольку его определение по умолчанию будет плохо сформировано из-за вызова unique_ptr конструктора удаленных копий.
  • Есть ли foo конструктор перемещения? GCC говорит "да", clang говорит "нет" .
  • Имеет ли foo конструктор удаленных перемещений? И GCC, и clang говорят "нет" .
  • Скопирован ли foo? Нет. Конструктор его копии удален.
  • Является ли foo подвижным? GCC говорит "да", clang говорит "нет" .

(Поведение аналогично, если рассматривать назначение вместо построения.)

Насколько я вижу, GCC верен. foo должен иметь конструктор перемещения, который выполняет перемещение по каждому элементу, который в with_copy случае вырождается в копию. Поведение Клана кажется довольно смешным: у меня есть совокупность с двумя подвижными членами, и все же моя совокупность - это неподвижный кирпич.

Кто прав?

4b9b3361

Ответ 1

С++ 11, или, скорее, n3485, [class.copy]/9:

Если определение класса X явно не объявляет конструктор перемещения, оно будет объявлено неявно как по умолчанию, если и только если

  • X не имеет объявленного пользователем конструктора копирования,
  • X не имеет объявленного пользователем оператора копирования копии,
  • X не имеет объявленного пользователем оператора назначения перемещения,
  • X не имеет объявленного пользователем деструктора и
  • конструктор перемещения не будет неявно определен как удаленный.

и/11:

Неявно объявленный конструктор copy/move является членом inline public своего класса. По умолчанию копия / move constructor для класса X определяется как удаленный (8.4.3), если X имеет:

  • [...]
  • для конструктора копирования, нестатического элемента данных ссылочного типа rvalue или
  • для конструктора перемещения, нестатического элемента данных или прямого или виртуального базового класса с типом, который не имеет конструктора перемещения и не может быть тривиально скопирован.

Поскольку with_copy не является тривиально-скопируемым, foo не будет иметь конструктор move (он будет определен как удаленный, поэтому он не будет объявлен неявно).


С++ 1y, или скорее github repo совершить e31867c0 с 2013-11-12; включая DR1402:

/9:

Если определение класса X явно не объявляет ход конструктор, одно будет объявлено как неявное, если и только если

  • X не имеет объявленного пользователем конструктора копирования,
  • X не имеет объявленного пользователем оператора копирования копии,
  • X не имеет объявленного пользователем оператора назначения перемещения и
  • X не имеет объявленного пользователем деструктора.

и/11:

Неявно объявленный конструктор копирования/перемещения является inline publicчлен его класса. По умолчанию конструктор copy/move для класса Xопределяется как удаленный (8.4.3), если X имеет:

  • [...]
  • для конструктора копирования, нестатический член данных ссылочного типа rvalue.

Конструктор перемещения по умолчанию, который определяется как удаленный, игнорируется разрешение перегрузки (13,3, 13,4).

Здесь foo будет иметь конструктор move.

Ответ 2

Я не совсем уверен, что вы протестировали, но он foo, безусловно, движется назначаемым и перемещается конструктивно. По общему признанию, это ничего не говорит о том, что конструктор перемещения или доступ к перемещению доступны, просто работает конструкция или присвоение из rvalue. Оба clang (clang version 3.5 (trunk 196718)) и gcc (версия gcc 4.9.0 20131031 (экспериментальная) (GCC)) согласна с этой оценкой. Это полный источник, который я пробовал:

#include <iostream>
#include <type_traits>
#include <memory>

struct with_copy {
    with_copy() = default;
    with_copy(with_copy const&) {}
    with_copy& operator=(with_copy const&) { return *this; }
};

struct foo {
    with_copy c;
    std::unique_ptr<int> p;
};

int main()
{
    std::cout << "move constructible: "
              << std::is_move_constructible<foo>::value << '\n';
    std::cout << "move assignable: "
              << std::is_move_assignable<foo>::value << '\n';
    foo f0;
    foo f1 = std::move(f0);
    f0 = std::move(f1);
}