Nikki Chumakov
Nikki Chumakov

Reputation: 1287

Perfect forwarding and copy elision of local variable

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;
}

Live example

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.
    }
}

Live example

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;
}

Live example

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

Answers (1)

user17732522
user17732522

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

Related Questions