Почему std::vector использует конструктор перемещения, хотя и объявлен как noexcept(false)
Везде, где я читаю в Интернете, настоятельно рекомендуется, чтобы, если я хочу, чтобы мой класс хорошо работал с std::vector
(т.е. семантика перемещения из моего класса использовалась std::vector
) Мне следует переместить конструктор delcare как noexcept (или noexcept(true)
).
Почему сделал std::vector
использовать это, хотя я отметил это noexcept(false)
как эксперимент?
#include <iostream>
#include <vector>
using std::cout;
struct T
{
T() { cout <<"T()\n"; }
T(const T&) { cout <<"T(const T&)\n"; }
T& operator= (const T&)
{ cout <<"T& operator= (const T&)\n"; return *this; }
~T() { cout << "~T()\n"; }
T& operator=(T&&) noexcept(false)
{ cout <<"T& operator=(T&&)\n"; return *this; }
T(T&&) noexcept(false)
{ cout << "T(T&&)\n"; }
};
int main()
{
std::vector<T> t_vec;
t_vec.push_back(T());
}
выход:
T()
T(T&&)
~T()
~T()
Зачем? Что я сделал не так?
Скомпилировано в gcc 4.8.2 с CXX_FLAGS, установленным в:
--std=c++11 -O0 -fno-elide-constructors
2 ответа
Вы не сделали ничего плохого.
Вы просто ошибочно подумали push_back
Пришлось избегать броска Move-Ctor: это не так, по крайней мере, для создания нового элемента.
Единственное место, где следует избегать бросания-перемещений / перемещений-назначений, - это перераспределение вектора, чтобы избежать перемещения половины элементов, а остальных на их исходных местах.
Функция имеет строгую гарантию исключительной безопасности:
Либо операция завершается успешно, либо происходит сбой, и ничего не изменилось.
Если vector::push_back
необходимо перераспределить свою память, сначала она выделяет новую память, а затем перемещает конструирует новый элемент в последнюю позицию. Если это приводит к освобождению новой памяти, и ничего не изменилось, вы получите надежную гарантию безопасности исключений, даже если конструктор перемещения может выдать.
Если он не выбрасывает, существующие элементы переносятся из исходного хранилища в новое хранилище, и вот где noexcept
спецификация конструктора перемещения имеет значение. Если при перемещении может возникнуть ошибка, а типом будет CopyConstructible, то существующие элементы будут скопированы, а не перемещены.
Но в вашем тесте вы только смотрите, как новый элемент вставляется в вектор, и всегда нормально использовать конструктор броска для этого шага.