If your object has multiple parameters and can take l or r value references the number of constructors jumps quickly. Can a little template trickery simplify the situation or does this cause issues that I am not aware of OR am I over-thinking it and there is standard way of simplifying this?
// In the following context I using std::string as an example o
// something that can be moved or copied, where we would prefer
// a move rather than a copy.
// With just two parameters.
// I need 4 constructors to do the correct thing (copy/move) correctly.
class Original
{
std::string val1;
std::string val2;
public:
Original(std::string const& v1, std::string const& v2): val1(v1), val2(v2) {}
Original(std::string const& v1, std::string&& v2): val1(v1), val2(std::move(v2)) {}
Original(std::string&& v1, std::string const& v2): val1(std::move(v1)), val2(v2) {}
Original(std::string&& v1, std::string&& v2): val1(std::move(v1)), val2(std::move(v2)) {}
};
If the parameter types were templates I could use perfect forwarding.
// This is a check to see if a template is the specific class.
// Or can be trivially constructed from the parameters.
template<typename Actual>
using ValidString = std::enable_if_t<std::is_trivially_constructible_v<std::string, Actual>, bool>;
// With these I can simplify my requirement of 4 constructors
// and simplify to a single templaed constructor that can do perfect forwarding.
class Alternative
{
std::string val1;
std::string val2;
public:
template<typename V1, typename V2, ValidString<V1> = true, ValidString<V2> = true>
Original(V1 v1, V2 v2): val1(std::forward<V1>(v1)), val2(std::forward<V2>(v2)) {}
};
Two questions:
The simplest way to do this in your specific case would be to do:
class Original
{
std::string val1;
std::string val2;
public:
Original(std::string v1, std::string v2) noexcept
: val1{ std::move(v1) }
, val2{ std::move(v2) }
{}
};
This is often good enough for constructors. It potentially does copy+move instead of just a copy, but most types are cheap to move anyway. One case where you may have to do your version is when the assignment is conditional. Something like:
template<typename T, typename Y>
void foo(T&& t, Y&& y)
{
if (condition)
{
varT = std::forward<T>(t);
varY = std::forward<Y>(y);
}
}
You can't get away with copy+move, because you definitely don't want an unnecessary copy.