简体   繁体   中英

Const reference to temporary object does not extend its lifetime

I have a class from which I create a temporary object. I can bind a const-reference to this temporary object, and it works as expected. However, if I call a member function, which returns std::move(*this), on this temporary object, and bind to this return value, it does not work as I expected. The short code below reproduces my problem and is self-explanatory.

#include <iostream>
#include <cstdlib>
#include <vector>

class myval {
    public:
    std::vector<int> i;
    myval(int i) : i({i}) {}
    myval(const myval& v) = delete;
    myval(myval&& v) : i(std::move(v.i)) {}
    myval&& addone() && {
        i[0]++;
        return std::move(*this);
    }

};

int main()
{
    //Object is moved, works like expected
    const auto moved_value = myval{7}.addone();
    std::cout << moved_value.i[0] << std::endl;

    //Const reference is supposed extend the lifetime of a temporary object
    const auto& u = myval{7};
    //Prints 7, as expected
    std::cout << u.i[0] << std::endl;

    const auto& v = myval{7}.addone();
    //Why does this print 0?
    std::cout << v.i[0] << std::endl;

    return 0;
}

Edit:

  1. Given what the explanation might be for this, is it possible to make this work so that the assignment " const auto& v =.... " works?

  2. Why does the following assignment work when my doesn't?

     const auto& s = std::string("hi")[1]; std::cout << s;

A const reference only extends the lifetime of function local temporaries. In the line

const auto& v = myval{7}.addone();

your reference is not binding to a temporary. addone returns by reference which means you are working with a lvalue instead of a temporary and consequently when the full expressions ends you are left with a reference to an object that no longer exists.


Regarding edits. To make

const auto& v = myval{7}.addone();

I would change addone to return by value like

myval addone() && {
    i[0]++;
    return std::move(*this);
}

This will then give you the correct behavior as it moves the object into a temporary that you then extend the lifetime.

For

const auto& s = std::string("hi")[1];
std::cout << s;

You code has undefined behavior and unfortunately you get what you expect. You are doing the same thing as the previous example and you get no lifetime extension of the temporary string you created.

Reference lifetime extension only works when applied directly to the forehead a temporary object.

There is no reference lifetime extension when applied to a reference.

myval&& addone() && {
    i[0]++;
    return std::move(*this);
}

the returns a reference. (rvalue and lvalue references are both kinds of reference)

const auto& v = myval{7}.addone();

this doesn't do reference lifetime extension, because we are binding a const auto& v to a reference, not a temporary.


How can we make it work? Well, rewriting addone 's signature we get:

myval addone() && {
    i[0]++;
    return std::move(*this);
}

now it returns a prvalue instead of a reference.

const auto& v = myval{7}.addone();

now this reference lifetime extends.

Your "why does this work" followup is because undefined behavior can do anything, including "appear to work". It isn't valid code.

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