Reputation: 28500
In Item 41, Scott Meyers writes the following two classes:
class Widget {
public:
void addName(const std::string& newName) // take lvalue;
{ names.push_back(newName); } // copy it
void addName(std::string&& newName) // take rvalue;
{ names.push_back(std::move(newName)); } // move it; ...
private:
std::vector<std::string> names;
};
class Widget {
public:
template<typename T> // take lvalues
void addName(T&& newName) // and rvalues;
{ // copy lvalues,
names.push_back(std::forward<T>(newName)); } // move rvalues;
} // ...
private:
std::vector<std::string> names;
};
What's written in the comments is correct, even if it doesn't mean at all that the two solutions are equivalent, and some of the differences are indeed discussed in the book.
In the errata, however, the author comments another difference not discussed in the book:
Another behavioral difference between (1) overloading for lvalues and rvalues and (2) a template taking a universal reference (uref) is that the lvalue overload declares its parameter
const
, while the uref approach doesn't. This means that functions invoked on the lvalue overload's parameter will always be theconst
versions, while functions invoked on the uref version's parameter will be theconst
versions only if the argument passed in isconst
. In other words, non-const
lvalue arguments may yield different behavior in the overloading design vis-a-vis the uref design.
But I'm not sure I understand it.
Actually, writing this question I've probably understood, but I'm not writing an answer as I'm still not sure.
Probably the author is saying that when a non-const
lvalue is passed to addName
, newName
is const
in the first code, and non-const
in the second code, which means that if newName
was passed to another function (or a member function was called on it), than that function would be required to take a const
parameter (or be a const
member function).
Have I interpreted correctly?
However, I don't see how this makes a difference in the specific example, since no member function is called on newName
, nor it is passed to a function which has different overloads for (not exactly: const
and non-const
parametersstd::vector<T>::push_back
has two overloads for const T&
arguments and T&&
arguments`, but an lvalue would still bind only to the former overload...).
Upvotes: 3
Views: 91
Reputation: 28500
In the second scenario, when a const std::string
lvalue is passed to the template
template<typename T>
void addName(T&& newName)
{ names.push_back(std::forward<T>(newName)); }
the instantiation results in the following (where I've removed the std::forward
call as it is, in practice, a no-op)
void addName(const std::string& newName)
{ names.push_back(newName); }
whereas if a std::string
lvalue is passed, then the resulting instance of addName
is
void addName(std::string& newName)
{ names.push_back(newName); }
which means that the non-const
version of std::vector<>::push_back
is called.
In the first scenario, when a std::string
lvalue is passed to addName
, the first overload is picked regardless of the const
-ness
void addName(const std::string& newName)
{ names.push_back(newName); }
which means that the const
overload of std::vector<>::push_back
is selected in both cases.
Upvotes: 0