Имеет ли этот код правильную вещь?
Да. C + + 11 добавил std::vector::push_back(T&&)
именно по этой причине.
Есть ли лучший способ сделать это?
Ваш fn(const vector<int> &v)
и fn(vector<int> &&v)
оба делают то же самое, выдвигая аргумент v
на конец vectorOfVectors
. Вместо ваших двух функций fn
вы могли бы использовать один шаблон функции, который использует совершенную пересылку.
template<typename T>
void fn(T &&v) {
vectorOfVectors.push_back(std::forward<T>(v));
}
Как это работает благодаря 11 reference collapsing rules и std::forward
С ++. Тип шаблона T
становится vector<int>&
в том случае, если v
является lvalue, но vector<int>&&
в том случае, если v
является rvalue. Правила обхода ссылок означают, что vector<int>& &&
становится vector<int>&
, а vector<int>&& &&
становится vector<int>&&
. Это делает именно то, что вы хотите, вызывая версию push_back
, которая делает копию в случае lvalue, но версия, которая выполняет движение в случае rvalue.
Одна из недостатков заключается в том, что иногда это может привести к интересной диагностике, когда вы ошиблись. («Интересно» здесь означает сотни строк непостижимого диагностического текста из g ++ или clang ++). Другой недостаток заключается в том, что шаблоны могут привести к тому, что «конвертеры ушли в дикую природу».
Для того же подхода к работе с пользовательскими классами необходимо определить конструкторы перемещения?
Не обязательно. Вы получите неявно объявленный конструктор перемещения, если класс не объявляет определяемый пользователем деструктор, конструктор копирования, оператор присваивания копии или оператор переадресации. Неявно объявленный конструктор перемещения будет определен как удаленный, если класс не имеет подвижных членов данных или получен из класса, который нельзя перемещать или удалять.
Для меня это слишком много, чтобы помнить. Я не знаю, является ли это хорошей практикой или плохим, но я начал использовать Foo(const Foo&)=default
, с аналогичными объявлениями для другого правила пяти функций. Я также квалифицирую конструкторы как explicit
во многих случаях, чтобы избежать проблем с «конвертерами».
Не могли бы вы рассказать о части «В некоторых случаях да»? И как работает шаблон? – SPMP
@ user2308211 - Я изменил «в некоторых случаях, да» на ваш конкретный случай. Обобщая немного, вы должны подумать о templatizing функции, если у вас есть 'f (T &)' и 'f (T &&)', которые являются копиями и вставками, идентичными друг другу, за исключением 'std :: move' или двух в версии перемещения 'f()'. –
«Тип шаблона' T' становится [...] 'vector &&' в случае, если 'v' является rvalue" - нет, в этом случае 'T' является просто' vector '. Справочная версия пересылки также улавливает все под солнцем (важно, если у вас есть перегрузки или нужно проверить, правильно ли форматируется 'f (что-то)'). Наконец, если проблема с типом дешево перемещаться (например, 'vector '), более простой подход - принять аргумент по значению, а затем безоговорочно переместить: 'void f (vector v) {vectorOfVectors.push_back (std :: шаг (v)); } '. –