Как спецификатор исключения в операторе присваивания перемещения может влиять на конструктор перемещения?

Я тестирую с GCC 5.2 и clang 3.6, оба в режиме C++14, и они дают одинаковый результат.

Для следующего кода

#include <iostream>
#include <type_traits>

struct S {
  // S& operator= (S&&) noexcept { return *this; }
};


int main() {
  std::cout << std::is_nothrow_move_constructible<S>::value
            << std::is_nothrow_move_assignable<S>::value;  
}

результат 11 получается. Но если раскомментировать оператор присваивания перемещения, вывод становится 01, Как мог явный noexcept спецификация на оператор присваивания перемещения может повлиять на конструктор перемещения?

5 ответов

Решение

Определив оператор присваивания перемещения, вы отключили конструктор перемещения из-за правила 5. Класс не is_nothrow_move_constructible так как он вообще не может быть перемещаем, этот конструктор больше не доступен, пока вы его не определите.

§12.8 Копирование и перемещение объектов класса

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

В случае, когда у вас не было пользовательского конструктора перемещения, оба были определены неявно и следовали приведенной ниже спецификации.

§15.4 Спецификации исключений

Неявно объявленная специальная функция-член должна иметь спецификацию исключения. Если f является неявно объявленным конструктором по умолчанию, конструктором копирования, конструктором перемещения, деструктором, оператором назначения копирования или оператором назначения перемещения, его неявная спецификация исключения определяет идентификатор типа T если и только если T допускается спецификацией исключений для функции, непосредственно вызываемой fнеявное определение; f должен разрешать все исключения, если любая функция, которую он непосредственно вызывает, разрешает все исключения, и fне должно допускать никаких исключений, если каждая функция, которую она непосредственно вызывает, не допускает никаких исключений.

Объявив о назначении перемещения, вы потеряли неявный конструктор перемещения.
Смотрите полную таблицу ниже.

введите описание изображения здесь

12.8/9:

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

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

  • X не имеет заявленного пользователем оператора копирования,

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

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

Объявляя оператор присваивания перемещения, вы вообще не допускаете, чтобы у класса был какой-либо конструктор перемещения.

Конструктор перемещения просто не генерируется в этом случае - он не имеет ничего общего с noexcept,

Из контекста:

Если для типа класса (struct, class или union) не заданы пользовательские конструкторы перемещения, и все из следующего верно:

  • нет никаких заявленных пользователем конструкторов копирования
  • нет заявленных пользователем операторов копирования
  • нет пользовательских операторов перемещения
  • нет деструкторов, объявленных пользователем (до C++14)

неявно объявленный конструктор перемещения не определен как удаленный из-за условий, подробно описанных в следующем разделе, тогда компилятор объявит конструктор перемещения как неявный встроенный открытый член своего класса с подписью T::T(T&&).

Определив оператор перемещения, вы подавили неявный конструктор перемещения. Вот почему std::is_nothrow_move_constructible выходит из строя. Предоставьте его, чтобы получить желаемый результат:

struct S {
  S(S&&) noexcept {}
  S& operator= (S&&) noexcept { return *this; }
};
Другие вопросы по тегам