Когда следует использовать форвард и двигаться?
У меня есть код, который работает с вектором:
template<typename T>
void doVector(vector<T>& v, T&& value) {
//....
v.push_back(value);
//...
}
Для обычного push_back
нужно ли использовать forward(value)
, move(value)
или просто value
(согласно новому С++ 11)? и как они влияют на производительность?
Например,
v.push_back(forward<T>(value));
Ответы
Ответ 1
Текущий код не будет компилироваться, если второй аргумент равен lvalue, потому что T&&
окажется X&
, что означает T
должно быть X&
, что в свою очередь означает, что std::vector<T>
станет std::vector<X&>
который не будет соответствовать первому аргументу, который равен std::vector<X> &
. Следовательно, это ошибка.
Я бы использовал два параметра шаблона:
template<typename T, typename V>
void doVector(vector<T> & v, V && value)
{
v.emplace_back(std::forward<V>(value));
}
Так как V
может быть другого типа из T
, поэтому emplace_back
имеет больше смысла, потому что не только он решает проблему, но и делает код более общим.: -)
Теперь следующее улучшение: поскольку мы используем emplace_back
, который создает объект типа T
из аргумента value
(возможно, используя конструктор), мы могли бы воспользоваться этим фактом и сделать его вариационной функцией:
template<typename T, typename ...V>
void doVector(vector<T> & v, V && ... value)
{
v.emplace_back(std::forward<V>(value)...);
}
Это еще более общее, поскольку вы можете использовать его как:
struct point
{
point(int, int) {}
};
std::vector<point> pts;
doVector(pts, 1, 2);
std::vector<int> ints;
doVector(ints, 10);
Надеюсь, что это поможет.
Ответ 2
-
forward(value)
используется, если вам нужно совершенное значение пересылки, сохраняя такие вещи, как l-value, r-value.
-
пересылка очень полезна, потому что она может помочь вам избежать написания нескольких перегрузок для функций, где существуют различные комбинации l-val, r-val и ссылочных аргументов
-
move(value)
на самом деле является типом оператора литья, который передает значение l в значение r
-
С точки зрения производительности, избегайте создания дополнительных копий объектов, которые являются основным преимуществом.
Таким образом, они действительно выполняют две разные вещи.
Когда вы говорите обычный push_back, я не уверен, что вы имеете в виду, вот две подписи.
void push_back( const T& value );
void push_back( T&& value );
первый, вы можете просто передать любой нормальный l-val, но для второго вам придется "переместить" l-val или переслать r-val. Имейте в виду, как только вы перемещаете l-val, вы не можете его использовать.
Для бонуса здесь есть ресурс, который, по-видимому, очень хорошо объясняет концепцию r-val-refs и других понятий, связанных с ними.
Как и другие, вы также можете переключиться на использование emplace, поскольку он действительно идеально подходит для аргументов конструктора объектов, что означает, что вы можете передать его, как хотите.
Ответ 3
- При передаче параметра ссылка на пересылку всегда используйте
std::forward
.
- При передаче параметра, имеющего значение r, используйте
std::move
.
например.
template <typename T>
void funcA(T&& t) {
funcC(std::forward<T>(t)); // T is deduced and therefore T&& means forward-ref.
}
void funcB(Foo&& f) {
funcC(std::move(f)); // f is r-value, use move.
}
Здесь отличное видео Скотта Мейерса, объясняющего пересылку ссылок (которые он называет универсальными ссылками), и когда использовать идеальную пересылку и/или std::move
:
С++ и Beyond 2012: Скотт Майерс - Универсальные ссылки в С++ 11
Также см. этот связанный вопрос для получения дополнительной информации об использовании std::forward
: Преимущества использования переадресации
Ответ 4
http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11
В этом видео, IMO, есть лучшее объяснение того, когда следует использовать std:: forward и когда использовать std:: move. В нем представлена идея универсальной ссылки, которая является ИМО чрезвычайно полезным инструментом для рассуждения о семантике перемещения.