Ветвление операторов присваивания со значениями вместо ссылок - PullRequest
14 голосов
/ 17 марта 2012

Этот вопрос возник из вопросов, поднятых этим ответом .

Обычно мы определяем операторы копирования для типа T как T& operator=(const T&), а операторы перемещения для типа T как T& operator=(T&&).

Однако что происходит, когда мы используем параметр-значение, а не ссылку?

class T
{
public:
  T& operator=(T t);
};

Это должно сделать T копируемым и перемещаемым. Однако я хочу знать, каковы языковые последствия для T?

В частности:

  1. Считается ли это оператором копирования для T, в соответствии со спецификацией?
  2. Считается ли это оператором присваивания перемещения для T, согласно спецификации?
  3. Будет ли T иметь сгенерированный компилятором оператор присваивания копии?
  4. Будет ли T иметь сгенерированный компилятором оператор присваивания перемещения?
  5. Как это влияет на классы черт типа std::is_move_assignable?

1 Ответ

14 голосов
/ 17 марта 2012

Большая часть этого описана в §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= будет правильно выполнять перемещения, но технически не считается оператором назначения перемещения. Это даже странно, если у класса нет конструктора копирования: у него будет оператор присваивания копии, который не делает копии, а только перемещает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...