Step 2 of 9
Perfect forwarding
A factory like std::make_unique<T>(args...) must pass its arguments on to T's constructor exactly as it received them: a temporary should stay a temporary (so it can be moved from), and a named variable should stay a named variable (so it's copied, not stolen). Getting that right is called perfect forwarding.
#include <iostream>
#include <string>
#include <utility>
struct Tag {
std::string text;
Tag(const std::string& t) : text(t) { std::cout << "copied '" << t << "'\n"; }
Tag(std::string&& t) : text(std::move(t)) { std::cout << "moved '" << text << "'\n"; }
};
template <typename T, typename... Args>
T build(Args&&... args) {
return T(std::forward<Args>(args)...);
}
int main() {
std::string name = "keep me";
Tag a = build<Tag>(name); // lvalue: must copy
Tag b = build<Tag>(std::string("temp")); // rvalue: may move
std::cout << "name is still '" << name << "'\n";
}
copied 'keep me'
moved 'temp'
name is still 'keep me'
How it works
- In a template,
Args&&is not a plain rvalue reference. It's a forwarding reference: it binds to anything and remembers whether the argument was an lvalue or an rvalue, by encoding it inArgs. - Inside the function,
argshas a name, so it's an lvalue no matter what was passed. Passing it on directly would always copy. std::forward<Args>(args)restores the original category: lvalue in, lvalue out; rvalue in, rvalue out. With a pack,std::forward<Args>(args)...does that for every argument.
Rules of thumb
std::movemeans "treat this as an rvalue, always".std::forward<T>means "keep whatever it originally was". Use it only on forwarding references, typically exactly once per argument.
Your task
v.emplace_back(...) already takes forwarded arguments and builds the element in place. Forward yours into it, and return v.back(), which is a reference to the new element. (Since C++17 emplace_back also returns that reference.)
Your turn: write template <typename T, typename... Args> T& emplace_into(std::vector<T>& v, Args&&... args) that constructs a T at the end of v from the forwarded arguments (with emplace_back) and returns a reference to it.
Previous: Parameter packs and fold expressions Next: Type traits