Reputation: 1287
I'd like to create a function that should return an rvalue reference when called with an rvalue reference as an argument. Otherwise, it should return a copy of its argument.
template <typename T>
constexpr inline auto is_const_reference_v = is_const_v<remove_reference_t<T>>;
template <typename T>
constexpr inline auto is_mutable_rvalue_reference_v =
is_rvalue_reference_v<T&&> && !is_const_reference_v<T>;
// Forward mutable rvalue refs
template <typename T, class =
enable_if_t<is_mutable_rvalue_reference_v<T>>
>
T&& copy_if_lvalue_or_const_ref (T&& t)
{
return move(t);
}
// Copy anything else
template <typename T>
decay_t<T> copy_if_lvalue_or_const_ref (T& t)
{
return t;
}
Next, I'd like to create a function that should save the result of copy_if_lvalue_or_const_ref in a temporary variable, call do_smth
method on it and return this variable.
Possible solution:
template <typename T>
decltype(auto) do_smth_and_pass(T&& t)
{
decltype(auto) tmp = copy_if_lvalue_or_const_ref(std::forward<T>(t));
tmp.do_smth();
if constexpr (is_mutable_rvalue_reference_v<T>)
{
return static_cast<T&&>(tmp);
}
else
{
return tmp; // copy elision does not work.
}
}
Unfortunately, copy elision does not work for lvalues, return tmp;
calls move constructor.
I found the solution using two functions:
template <typename T, enable_if_t<is_mutable_rvalue_reference_v<T>>* = nullptr>
decltype(auto) do_smth_and_pass(T&& t)
{
decltype(auto) tmp = copy_if_lvalue_or_const_ref(std::forward<T>(t));
tmp.do_smth();
return static_cast<T&&>(tmp);
}
template <typename T, enable_if_t<!is_mutable_rvalue_reference_v<T>>* = nullptr>
decay_t<T> do_smth_and_pass(T&& t)
{
decltype(auto) tmp = copy_if_lvalue_or_const_ref(std::forward<T>(t));
tmp.do_smth();
return tmp;
}
I do not like it, though. It is not generic enough. Is it possible to make the same with a single function?
Upvotes: 0
Views: 216
Reputation: 76859
If you want to call a member function on the object before returning it, then there is no way to guarantee copy elision.
Both of your attempts are equivalent: Both allow for, but do not guarantee, named return value optimization (NVRO) to be applied so that the result object of the function call is the same object as tmp
. It is up to the compiler under which circumstances it applies this optimization.
Upvotes: 0