73 Advanced The Hundred
The Overload That Swallowed Everything
you wrote a std::string overload; a std::string walks straight past it
#include <iostream>
#include <string>
void handle(const std::string& s) { std::cout << "const ref: " << s << '\n'; }
template <class T> void handle(T&& s) { std::cout << "template: " << s << '\n'; }
int main() {
std::string name = "Ada";
const std::string title = "Countess";
handle(name); // a plain std::string — surely the string overload
handle(title);
handle("Grace");
}
Run it. Which overload gets each of the three calls?
Answer
Only the const one reaches the overload you wrote for strings.
Why¶
T&& on a deduced template parameter is not an rvalue reference — it is a forwarding
reference: for the non-const lvalue name, T deduces to std::string&, and reference
collapsing (std::string& && → std::string&) leaves a parameter binding it exactly. Both
candidates bind without a conversion, but when two reference bindings differ only in const,
the less cv-qualified one wins — so the template takes it before "prefer the non-template"
applies. The literal loses too: T deduces to const char (&)[6], an exact match against a
user-defined conversion. title escapes only because T deduces to const std::string&,
making the parameters identical — and in that tie the non-template wins.
Neither -Wall nor -Wextra says a word about any of it. As a constructor, the same
template out-ranks the copy constructor for any non-const object.
The fix¶
Constrain it (<type_traits>), so it stops competing for the types you already handle:
template <class T, class = std::enable_if_t<!std::is_convertible_v<T, std::string>>>
void handle(T&& s) { std::cout << "template: " << s << '\n'; }
All three calls now print const ref:, while handle(42) still reaches the template; in
C++20 the guard reads requires (!std::convertible_to<T, std::string>), from <concepts>.
Simplest of all: never overload on a forwarding reference — give that template its own name.
Takeaway: a forwarding reference is the greediest overload in the language — it binds
anything exactly, and between two exact reference bindings the less const one wins.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo