简体   繁体   中英

binding const reference to rvalue reference

I have this private member function below, (part of class template, Heap ) :

template <typename Task, typename Priority>
const size_t& Heap<Task, Priority>::parent_of(const size_t& index) const
{
    // ** warning C4172: returning address of local variable or temporary
    return (this_index-1)/2;
}

And i am calling it from other function, like below:

template <typename Task, typename Priority>
void Heap<Task, Priority>::bubble_up(const size_t&   start_index)
{
    ....
    if (priority_of(start_index) > priority_of((parent_of(start_index)))) 
    { 
        ... do work ...
        //call recursively the bubble_up
        bubble_up(parent_of(start_index));
    }
    ...
}

The problem is, with priority_of function argument index get corrupted or releases in the 2nd call of the recursion:

template <typename Task, typename Priority>
const Priority& Heap<Task, Priority>::priority_of(const size_t& index) const
{
    return vec.at(index).second;
}

Now, VS have warned me that i am returning address of local variable or temporary (rvalue) in function parent_of , which at the end, this behavior make sense, because when control exists/return from parent_of all local variables including function arguments released!

Now, when changing the function parent_of to return by value (not by const ref), things get to work!

I came from C++98, (so all the rvalue reference is not clear to me) the question is: when and how should i use the rvalue reference ( && ) to overcome this ? can i reference (including changing its value) this temp object allocated by the compiler and return reference to it (to be used as return value)?

If you want to preserve the lifetime semantics of a return value depending on the value category of the returned expression, you can't return a const& , or even a && since you will face issues with dangling references.

Instead, you can use decltype(auto) for the return type in order to deduce the appropriate value category of the returned expression:

template <typename Task, typename Priority>
decltype(auto) Heap<Task, Priority>::priority_of(const size_t& index) const
{
    decltype(auto) result = vec.at(index).second;
    return decltype(result)(result);
}

Now the return type will deduce the correct value-category, ie l-values for l-value references, and r-values for pr-values (temporaries), and x-values (expiring values).

The cast to decltype(result) in the return statement is used to cast the expression to the appropriate type based on the type of the entity named by the id-expression result .

You need to use this technique for all functions in the call stack where you want to preserve the lifetime semantics.

You can think of this technique as perfect-forwarding, but in the opposite direction, ie up the call stack, instead of down.

This answer is based on the technique described in this entertaining lightning talk .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM