An unnecessary memory allocation is much more of a performance hit than suboptimal calling convention.
But yeah, in a c++ codebase, good modern practices are often verbose and clunky.
IMO it's hard to beat pass by value considering both performance and cognitive load.
...but that requires the argument to be a type from a template D: so you'd have to write:
template<typename String = std::string>
void dosomething(String &&str)
...and that's not quite right either, since you'd want it to be either an rvalue reference or a const lvalue referencestd::string in the case you cited is only really relevant if you std::move() into it and you would otherwise incur a string copy. Yes, it's bigger than 16 bytes (24 bytes), but that pales in comparison to the alternative.
(Taking std::string&& would eliminate the possibility of misuse / accidental copies, but that pattern is generally discouraged by Google's style guide for various reasons.)
Also, just because you see a certain pattern an awful lot even at Google doesn't mean that it's best practice -- there are plenty of instances of protobufs being passed by value...
1. `foo(T)` indicates polymorphic over both `T::T(const T&)` and `T::T(T&&)`. This gives you benefits of both pass-by-move (using `std::move` as needed), or copy.
2. Usage of `foo(T&&)` signals code-smell or an anti-pattern as `foo(T)` should be used instead unless it is perfecting forwarding / universal reference `template <typename T> foo(T&&)`.