初始化右值引用成员

时间:2020-01-19 21:41:11

标签: c++

在以下情况下,我尝试初始化右值引用成员:struct A是聚合,而class B具有用户定义的构造函数,因此它不是聚合。根据此处的cppreference

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

我的struct A中的引用应该正确初始化,并且临时字符串应该扩展,但是不是。
对于我的class B,在同一cppreference页面中:

绑定到构造函数初始化器列表中的引用成员的临时绑定仅在构造函数退出之前一直存在,只要对象不存在即可。 (注意:从DR 1696开始,这种初始化是错误的)。 (直到C ++ 14)

但是我仍然遇到/std:c++latest的MSVC问题。我想念什么吗?

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
}

编辑:请首先告诉我在这两种情况下是否悬挂参考。对于MSVC,该版本是最新版本v19.24.28315

EDIT2:好的,我实际上对cppreference的解释感到困惑。我没有特别要求C ++ 20。请告诉我,哪个C ++版本的代码格式正确。

EDIT3:是否有正确的方法来初始化非聚合类的右值引用成员?由于将其绑定到成员初始化程序列表中的临时文件的格式不正确(到C ++ 14为止,这是否意味着自C ++ 14起就好了?),并将临时文件传递给构造函数并期望rvalue无法将其生存期延长两次

1 个答案:

答案 0 :(得分:4)

您的类A是聚合类型,但不是B,因为它具有用户提供的构造函数和私有非静态成员。

因此,A a{ std::string{"hello world"} };是聚合初始化,确实通过引用绑定将临时生存期延长到a

另一方面,B不是聚合初始化。它调用用户提供的构造函数,该构造函数将传递引用。传递引用不会延长临时生存期。 std::string的构造函数退出时,B对象将被破坏。

因此,以后使用a具有明确的行为,而b则具有未定义的行为。

这适用于C ++ 11以来的所有C ++版本(在此程序明显不正确之前)。

如果您的编译器正在为a打印垃圾(从程序中删除b后不再具有未定义的行为),那么这是编译器中的错误。


关于问题的编辑:

无法通过绑定到非聚合类的引用成员来延长临时项的寿命。

完全依赖此生存期扩展非常危险,因为如果将来碰巧使该类不再聚合,则可能不会收到任何错误或警告。

如果您希望类始终保留对象直到其生存期结束,则只需按值而不是按引用存储它即可:

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