class EntityBase {
public:
EntityBase() { std::cout << "EntityBase constructor called." << std::endl; }
EntityBase(const std::string& name) : m_name(name) { std::cout << "EntityBase constructor string called." << std::endl; }
EntityBase(std::string&& name) : m_name(std::move(name)) { std::cout << "EntityBase constructor string&& called." << std::endl; }
~EntityBase() { std::cout << "EntityBase destructor called." << std::endl; }
private:
std::string m_name;
};
In the second constructor , I am already taking in name as an rvalue reference, so do I need to use the std::move(name) call ?
How can I write a single constructor which can take both lvalue and rvalue references and based on the type of reference move or copy the argument ?
I expect that we don't need to use the move constructor as we have already captured the type of the argument as being an rvalue reference.
Yes, because
namehas a name, when used as an expression, its value category is lvalue1. It doesn't matter it's declared type (which is a different thing from the value category of an expression) isrvalue reference.You need a template,
You may wish to add additional SFINAE checks to make sure
Stringis really the type you expect it to be. Also, for single argument constructors, you should usually make the constructorexplicit.Alternatively, if you don't mind an extra move in the lvalue case, you can just take the string by value and then always perform a move, this will correctly handle both lvalue and rvalues.
1 "Anything that has a name is lvalue" is a rule that works well enough in practice in most of the cases. For the few exceptions, refer to the canonical SO question What are rvalues, lvalues, xvalues, glvalues, and prvalues?