In Item 25 of More Effective C++, Meyer compares this code:
class Widget {
public:
template<typename T>
void setName(T&& newName) // newName is
{ name = std::forward<T>(newName); } // universal reference
…
};
To this code:
class Widget {
public:
void setName(const std::string& newName) // set from
{ name = newName; } // const lvalue
void setName(std::string&& newName) // set from
{ name = std::move(newName); } // rvalue
…
};
One drawback he outlines with the second snippet vs the first is performance:
With the version of
setName
taking a universal reference, the string literal"Adela Novak"
would be passed tosetName
, where it would be conveyed to the assignment operator for thestd::string
insidew
.w
’s name data member would thus be assigned directly from the string literal; no temporarystd::string
objects would arise. With the overloaded versions ofsetName
, however, a temporarystd::string
object would be created forsetName
’s parameter to bind to, and this temporarystd::string
would then be moved intow
’s data member. A call tosetName
would thus entail execution of onestd::string
constructor (to create the temporary), onestd::string
move assignment operator (to movenewName
intow.name
), and onestd::string
destructor (to destroy the temporary). That’s almost certainly a more expensive execution sequence than invoking only thestd::string
assignment operator taking aconst char*
pointer.
But why can't you just make templates out of them, so the cast to std::string
is unnecessary? As in this code, which seems to compile and run just fine with suitable adjustments:
class Widget {
public:
template<typename T>
void setName(const T& newName) // set from
{ name = newName; } // const lvalue
template<typename T>
void setName(T&& newName) // set from
{ name = std::move(newName); } // rvalue
…
};
NB: It's not that I'm arguing for this overloading strategy. Meyer's other arguments are compelling. I would just like some help understanding this particular argument about performance.