I encounter the pass by value and move idiom quite often:
struct Test
{
Test(std::string str_) : str{std::move(str_)} {}
std::string str;
};
But it seems to me that passing by either const reference or rvalue reference can save a copy in some situations. Something like:
struct Test1
{
Test1(std::string&& str_) : str{std::move(str_)} {}
Test1(std::string const& str_) : str{str_} {}
std::string str;
};
Or maybe using a forwarding reference to avoid writing both constructors. Something like:
struct Test2
{
template<typename T> Test2(T&& str_) : str{std::forward<T>(str_)} {}
std::string str;
};
Is this the case? And if so, why is it not used instead?
Additionally, it looks like C++20 allows the use of auto parameters to simplify the syntax. I am not sure what the syntax would be in this case. Consider:
struct Test3
{
Test3(auto&& str_) : str{std::forward<decltype(str_)>(str_)} {}
std::string str;
};
struct Test4
{
Test4(auto str_) : str{std::forward<decltype(str_)>(str_)} {}
std::string str;
};
Edit:
The suggested questions are informative, but they do not mention the "auto" case.
But it seems to me that passing by either const reference or rvalue reference can save a copy in some situations.
Indeed, but it requires more overloads (and even worst with several parameters).
Pass by value and move idiom has (at worst) one extra move. which is a good trade-off most of the time.
maybe using a forwarding reference to avoid writing both constructors.
Forwarding reference has its own pitfalls:
{..}
syntax for parameter as {..}
has no type.
Test2 a({5u, '*'}); // "*****"
would not be possible.requires
or SFINAE).
Test2 b(4.2f); // Invalid, but `std::is_constructible_v<Test2, float>` is (falsely) true.
would produces error inside the constructor, and not at call site (so error message less clear, and SFINAE not possible).Test2 c(a); // Call Test2(T&&) with T=Test2&
// instead of copy constructor Test2(const Test2&)
would produce error, as std::string
cannot be constructed from Test2&
.