C/C++ Arena

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

Rules of thumb

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