Step 6 of 7
Sink parameters and emplace_back
When a function or constructor is going to keep a string (store it in a member or a container), how should it take the parameter?
const std::string&avoids a copy on the way in, but then storing it must copy.- The sink pattern: take it by value, and
std::moveit into place. The caller decides whether that by-value parameter is filled by a copy (if they still need their string) or by a move (if they pass a temporary or usestd::move).
#include <iostream>
#include <string>
#include <utility>
#include <vector>
class Contact {
public:
Contact(std::string name, std::string phone)
: name_(std::move(name)), phone_(std::move(phone)) {}
std::string line() const { return name_ + ": " + phone_; }
private:
std::string name_;
std::string phone_;
};
int main() {
std::vector<Contact> book;
std::string mine = "Ada";
book.emplace_back(mine, "555-0101"); // mine is copied once; we still have it
book.emplace_back("Grace", "555-0199"); // built in place from the literals
book.push_back(Contact("Linus", "555-0142")); // builds a temporary, then moves it in
for (const auto& c : book) std::cout << c.line() << "\n";
std::cout << "still have " << mine << "\n";
}
Ada: 555-0101
Grace: 555-0199
Linus: 555-0142
still have Ada
The cost for each kind of caller
- Passing a variable you still need: exactly one copy (into the parameter), then a cheap move into the member. You can't do better, since two strings must exist.
- Passing a temporary or
std::move(x): zero copies, just moves.
emplace_back vs push_back
push_back(obj)takes a finished object and copies or moves it into the vector.emplace_back(args...)passesargsstraight to the element's constructor, building it inside the vector's memory. No temporary object at all.
Use emplace_back when you have constructor arguments rather than an existing object.
Common mistake
Taking a parameter by value and then not moving it: name_(name) copies again. The std::move in the initializer is what makes the sink pattern pay off.
Your task
Give Player a sink constructor, and write void recruit(std::vector<Player>& team, std::string name) that adds a player using emplace_back and moves the name.
Challenge 2
Give Team two sink functions: set_name(std::string name) and add_tag(std::string tag), which moves the tag into the tags_ vector. Both must move their parameter, so a caller who passes std::move(s) never pays for a copy.
Challenge 3
Write void add_points(std::map<std::string, int>& scores, std::string name, int pts). If name is new, insert it with pts, moving the name into the map. If it's already there, add pts to the existing score. Use try_emplace, which returns {iterator, inserted} and only uses its arguments when the key is new.
Previous: Moved-from objects Next: Challenge: a move-aware log