std::vector<T>::push_back()
требует T
для удовлетворения концепции MoveInsertable (которая фактически включает в себя распределитель Alloc
).Это связано с тем, что push_back
в векторе может потребоваться увеличить вектор, переместив (или скопировав) все элементы, которые в нем уже есть.
Если вы объявите ход c'or для T
как удаленный,тогда, по крайней мере для распределителя по умолчанию (std::allocator<T>
), T
больше не будет MoveInsertable .Обратите внимание, что это отличается от случая, когда конструктор перемещения не был объявлен, например, потому что неявно генерировалась только копия c'or, или потому что была объявлена только копия c'tor, и в этом случае тип по-прежнему MoveInsertable , но на самом деле вызывается копия c'tor (это немного нелогично, TBH).
Причина, по которой на самом деле никогда не вызывается move c'tor, заключается в том, что вы вставляете только один элемент,и поэтому перемещение существующих элементов не требуется во время выполнения.Важно, что ваш аргумент push_back
сам по себе является lvalue и поэтому ни в коем случае не копируется и не перемещается.
ОБНОВЛЕНИЕ : мне пришлось взглянуть на это подробнеевнимательно (спасибо отзывам в комментариях).Версии MSVC, которые отклоняют код, на самом деле правильны (очевидно, и 2015, и до 2018 года делают это, но 2017 принимает код).Поскольку вы звоните push_back(const T&)
, T
должен быть CopyInsertable .Однако CopyInsertable определяется как строгое подмножество MoveInsertable .Поскольку ваш тип не является MoveInsertable , он также не является CopyInsertable по определению (обратите внимание, что, как объяснено выше, тип может удовлетворять обеим концепциям, даже если он только копируемый, так какдо тех пор, пока ход c'or явно не удален).
Разумеется, возникает ряд вопросов: (A) Почему GCC, Clang и некоторые версии MSVC все равно принимают код, и (B)нарушают ли они стандарт?
Что касается (A), то нет другого способа узнать, кроме как поговорить с разработчиками стандартной библиотеки или посмотреть на исходный код ... я думаю, что это простонет необходимости осуществлять эту проверку, если все, что вас волнует, - это заставить юридические программы работать.Перемещение существующих элементов во время push_back
(или reserve
и т. Д.) Может происходить любым из трех способов в соответствии со стандартом:
- Если
std::is_nothrow_move_constructible_v<T>
, то элементы не перемещаются (и операция строго безопасна для исключений). - В противном случае, если
T
равен CopyInsertable , то элементы копируются (и операция строго безопасна для исключений). - В противном случае элементы перемещаются (и эффекты исключений, возникающих при перемещении c'or, не определены).
Поскольку ваш тип не nothrow move c'tible, но имеет копию c'torВторой вариант может быть выбран.Это снисходительно, поскольку для MoveInsertable проверка не производится.Это может быть недосмотр реализации, или я мог быть намеренно проигнорирован.(Если тип не MoveInsertable , то весь вызов некорректен, поэтому эта пропущенная проверка не влияет на правильно сформированные программы.)
Что касается (B), IMOреализации, которые принимают код, нарушают стандарт, потому что они не производят диагностику.Это связано с тем, что реализация требуется для выдачи диагностики для плохо сформированных программ (это включает в себя программы, использующие языковые расширения, предоставляемые реализацией), если в стандарте не указано иное, в данном случае это не так..