Forwarding references are a special kind of references that both ignore and preserve the value category of a function argument,
making it possible to forward it by using std::forward
or std::forward_like
.
Any code using such a reference for any purpose other than forwarding is actually ignoring the rvalue-ness and const-ness of the associated
parameter.
Noncompliant code example
class Registry {
std::vector<std::string> names;
public:
template <typename StringLike>
void addName(StringLike&& arg) {
names.emplace_back(arg); // Not forwarded
}
};
void example() {
Registry r;
std::string name = "example";
r.addName(std::move(name));
std::cout << "name:" << name << std::endl;
// output is "name:example"
}
In this example, the intent is to move the content of name
into the vector, but instead a copy is made.
Compliant solution
class Registry {
std::vector<std::string> names;
public:
template <typename StringLike>
void addName(StringLike&& arg) {
names.emplace_back(std::forward<StringLike>(arg));
}
};
void example() {
Registry r;
std::string name = "example";
r.addName(std::move(name));
std::cout << "name:" << name << std::endl;
// output can be anything: name has been moved-from
}
std::forward_like
is available since C++23 and is useful when you want to forward a subobject of a forwarding reference.
class Registry {
// ...
template <typename PairOfStrings>
void addNames(PairOfStrings&& arg) {
addName(std::forward_like<PairOfStrings>(arg.second)); // We don't care about arg.first
}
};