Почему конструктор перемещения не объявлен и не удален с помощью 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 Copyable? Да. Его конструктор копирования используется для копий.
  • Является with_copy Подвижный? Да. Его конструктор копирования используется для ходов.

… а теперь хитрые.

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

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

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

Кто прав?

16

Решение

C ++ 11, а точнее n3485, [class.copy] / 9:

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

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

и / 11:

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

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

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


C ++ 1y, точнее github repo commit e31867c0 от 2013-11-12; включения DR1402:

/ 9:

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

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

и / 11:

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

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

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

Вот, foo будет иметь движение-конструктор.

8

Другие решения

Я не совсем уверен, что вы проверяли, но это foo безусловно, и двигаться назначаемым и двигаться конструктивно. Следует признать, что это ничего не говорит о конструкторе перемещения или доступном назначении перемещения, просто работает эта конструкция или назначение из rvalue. И то и другое лязг (лямка версии 3.5 (багажник 196718)) и НКУ (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);
}
7

По вопросам рекламы [email protected]