Большая часть этого описана в §12.8. Пункт 17 определяет, что считается объявленными пользователем операторами копирования:
Объявленный пользователем оператор присваивания копии X::operator=
является нестатической не шаблонной функцией-членом класса X
с ровно одним параметром типа X
, X&
, const X&
, volatile X&
, или const volatile X&
.
Пункт 19 определяет, что считается оператором назначения перемещения, объявленным пользователем:
Объявленный пользователем оператор присваивания перемещения X::operator=
является нестатичным
не шаблонная функция-член класса X
с ровно одним параметром
введите X&&
, const X&&
, volatile X&&
или const volatile X&&
.
Таким образом, он считается оператором назначения копирования, а не оператором перемещения.
В параграфе 18 указано, когда компилятор генерирует операторы копирования:
Если определение класса явно не объявляет присвоение копии
оператор, один объявлен неявно. Если определение класса объявляет
конструктор перемещения или оператор присваивания перемещения, неявно
объявленный оператор присвоения копии определяется как удаленный; в противном случае это
определяется как дефолт (8.4). Последний случай считается устаревшим, если
класс имеет объявленный пользователем конструктор копирования или объявленный пользователем
деструктор.
Параграф 20 сообщает нам, когда компилятор генерирует операторы присваивания перемещения:
Если определение класса X явно не объявляет ход
оператор присваивания, один будет неявно объявлен по умолчанию, если
и только если
[...]
- X не имеет объявленного пользователем оператора копирования,
[...]
Поскольку класс имеет объявленный пользователем оператор копирования, ни один из неявных не будет сгенерирован компилятором.
std::is_copy_assignable
и std::is_move_assignable
описаны в таблице 49 как имеющие то же значение, что и соответственно is_assignable<T&,T const&>::value
и is_assignable<T&,T&&>::value
. Эта таблица говорит нам, что is_assignable<T,U>::value
равно true
, когда:
Выражение declval<T>() = declval<U>()
хорошо сформировано при обработке
как неоцененный операнд (пункт 5). Проверка доступа выполняется как
если в контексте, не связанном с T
и U
. Только срок действия
Непосредственный контекст выражения присваивания рассматривается.
Поскольку declval<T&>() = declval<T const&>()
и declval<T&>() = declval<T&&>()
правильно сформированы для этого класса, он по-прежнему считается назначаемым для копирования и назначаемым для перемещения.
Как я уже упоминал в комментариях, любопытно, что при наличии конструктора перемещения operator=
будет правильно выполнять перемещения, но технически не считается оператором назначения перемещения. Это даже странно, если у класса нет конструктора копирования: у него будет оператор присваивания копии, который не делает копии, а только перемещает.