Im confuesd about the usage of std::move in class constructors.
I want to pass a value to a class and check in the constructor if a valid value is passed. If not an exception is thrown.
Before i used to pass the value by const reference:
#include <string>
class Foo {
public:
Foo(const std::string& value)
:m_value{ value }
{
if (value == "Foo") {
throw std::invalid_argument("Invalid");
}
}
private:
std::string m_value;
};
Now i read in modern c++ it is better to pass by value and move the passed value into the class variable:
#include <string>
class Foo {
public:
Foo(std::string value)
:m_value{ std::move (value) }
{
// Now m_value must be used because value is empty if reference
//if(value == "Foo") -> Desaster value is moved away = empty
if (m_value == "Foo") {
throw std::invalid_argument("Invalid");
}
}
private:
std::string m_value;
};
So far so good i can just refer to the class variable to do my error checking. Now i started to modernize a codebass which has a class hierachy were an value is already defined in the base class. The child class basically performs additional checks on the variable and throws.
Here a simplified example:
#include <string>
class Bar {
public:
Bar(std::string value)
:m_value{std::move(value))}
{
// value is "empty" here because its moved into m_value
if (m_value == "Foo") {
throw std::invalid_argument("Invalid");
}
}
std::string get_value() const { return m_value; }
void set_value(const std::string& value)
{
m_value = value;
}
private:
std::string m_value;
};
// Version 1
// We have to ask several times the base class to get the compare value
class Foo : public Bar {
public:
Foo(std::string value)
:Bar{ std::move(value) }
{
if (get_value() == "BAR" ||
get_value() == "Bar" || // perform several checks here
get_value() == "bar") {
throw std::invalid_argument("Invalid");
}
}
};
// Version 2
// We need to make a copy to check. Doesn't this ruin the purpose of
// std::move by saving a copy??
class Foo : public Bar {
public:
Foo(std::string value)
:Bar{ std::move(value) }
{
auto check_value = get_value();
if (check_value == "BAR" ||
check_value == "Bar" || // perform several checks here
check_value == "bar") {
throw std::invalid_argument("Invalid");
}
}
};
// Version 3
// doesn't work except we provide in the base class a default constructor
class Foo : public Bar {
public:
Foo(std::string value)
//Error here we need to provide a default constructor
{
if (value == "BAR" ||
value == "Bar" || // perform several checks here
value == "bar") {
throw std::invalid_argument("Invalid");
}
set_value(std::move(value));
}
};
So my question is, which approach is the best. Or how can this be handled better?
std::string_view-parameters overstd::string const&.std::stringorstd::string&&is only superior when you have an expiring value anyway.std::string_viewdo you still usestd::move?std::string&&and move, as well asstd::string_viewand copy.