简体   繁体   中英

Initialize rvalue reference member

I am trying to initialize a rvalue reference member in the following situations: struct A is a aggregate and class B has a user defined constructor so it is not an aggregate. According to cppreference here,

struct A {
  int&& r;
};
A a1{7}; // OK, lifetime is extended
A a2(7); // well-formed, but dangling reference

The reference in my struct A should be correctly initialized and the temporary string should be extended, but it's not.
For my class B , in the same cppreference page:

a temporary bound to a reference member in a constructor initializer list persists only until the constructor exits, not as long as the object exists. (note: such initialization is ill-formed as of DR 1696). (until C++14)

But I am still getting problem with MSVC with /std:c++latest . Am I missing anything?

struct A
{
    std::string&& ref;
};

class B
{
    std::string&& ref;
public:
    B(std::string&& rref):ref(std::move(rref)){}
    void print() {std::cout<<ref<<'\n';}
};

int main()
{
    A a{ std::string{"hello world"} };
    std::cout<<a.ref<<'\n'; //garbage in MSVC with c++latest, correct in GCC9.2
    B b{ std::string{"hello world"} };
    b.print(); //same
}

EDIT: Please tell me if I am getting dangling reference in these two cases in the first place. And for MSVC, the version is the latest update, v19.24.28315

EDIT2: OK I am actually confused by cppreference's explanation. I am not asking specificly for C++20. Please tell me under which C++ version, the code is well-formed or not.

EDIT3: Is there a proper way to initialize a rvalue reference member of a non-aggregate class? Since bind it to a temporary in a member initializer list is ill-formed (until C++14, does it mean it is good since C++14?) and passing a temporary to a constructor expecting an rvalue cannot extend its lifetime twice.

Your class A is an aggregate type, but B isn't, because it has a user-provided constructor and a private non-static member.

Therefore A a{ std::string{"hello world"} }; is aggregate initialization which does extend the lifetime of the temporary through reference binding to that of a .

On the other hand B is not aggregate initialization. It calls the user-provided constructor, which passes on the reference. Passing on references does not extend the lifetime of the temporary. The std::string object will be destroyed when the constructor of B exits.

Therefore the later use of a has well-defined behavior, while that of b will have undefined behavior.

This holds for all C++ versions since C++11 (before that the program would be obviously ill-formed).

If your compiler is printing garbage for a (after removing b from the program so that it doesn't have undefined behavior anymore), then this is a bug in the compiler.


Regarding edit of question:

There is no way to extend the lifetime of a temporary through binding to a reference member of a non-aggregate class.

Relying on this lifetime extension at all is very dangerous, since you would likely not get any error or warning if you happen to make the class non-aggregate in the future.

If you want the class to always retain the object until its lifetime ends, then just store it by-value instead of by-reference:

class B
{
    std::string str;
public:
    B(std::string str) : str(std::move(str)) {}
    void print() { std::cout << str << '\n'; }
};

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