c++ 作为类成员的r值引用

ecfdbz9o  于 2023-02-26  发布在  其他
关注(0)|答案(1)|浏览(144)

test<long double&>包含引用,因此test<long double&>具有指针大小。
我认为test<long double>包含long double,因此,我期望它的大小等于10(12对齐)字节。
但是,我错了,test<long double>也有指针(g++)的大小。
所以问题是:

  • 代码是否正确,还是未定义的行为?
  • 如果代码正确,由于long double不包含在对象test<long double>中,它存储在何处?
  • 当我说test<long double>是容器而test<long double&>是视图时,对吗?
#include <iostream>

template<typename T>
struct test
{
    T &&val;
};

long double get() { return 6; }
long double &get_ref()
{
    static long double a = 5;
    return a;
}

int main()
{
    test<long double> t_by_value{get()};       // THIS IS A CONTAINER
    test<long double&> t_by_ref{get_ref()};    // THIS IS A VIEW
    std::cout << t_by_value.val << " " << t_by_ref.val << "\n";
    // output: 6 5
    std::cout << sizeof(t_by_value) << " " << sizeof(t_by_ref) << "\n";
    // output: 4 4 (on a 32 bit system)
}
sdnqo3pr

sdnqo3pr1#

代码是否正确,还是未定义的行为?
类定义本身是正确的,但是使用它(或任何其他有引用成员变量的类)很容易有悬空引用,因此如果没有实现适当的保护逻辑,就会出现未定义的行为。例如,在代码中,这一行:

std::cout << t_by_value.val << " " << t_by_ref.val << "\n";

引用t_by_value的悬空引用val,因此是UB。

    • EDIT**:感谢apple apple的评论,指出了该规则的另一个例外,要求在类有聚合初始化时直接绑定引用成员数据类型,[class. temporary ]/6.10:

如果引用绑定到的glvalue是通过以下方式之一获取的,则引用绑定到的临时对象或作为引用绑定到的子对象的完整对象的临时对象将在引用的生存期内持续存在:
...
绑定到从括号中的表达式列表(dcl.init(https://timsong-cpp.github.io/cppwp/n4861/dcl.init))初始化的类类型聚合的引用元素的临时对象持续存在,直到包含表达式列表的完整表达式完成为止。
因此,问题中提供的代码片段是100%正确的,没有任何未定义的行为。
这个长双精度浮点数存储在哪里,因为它不包含在对象test<long double>中?
在您的场景中,它存储在传递给聚合构造函数的临时变量中(其生存期延长到封闭作用域的末尾):

test<long double> t_by_value{get()};       // THIS IS A CONTAINER

当我说test<long double>是一个容器而test<long double&>是一个视图时,对吗?
在这个例子中不是这样的。为了更好地理解它,你需要考虑模板是如何推导它们的类型的。当你有一个模板变量声明如下:

T&& val;

你有所谓的普遍参照,它只能是两种类型中的一种:右值引用或左值引用。对于任何左值,类型都将变为T&对于任何x值或右值,类型都将变为T&&

    • EDIT**:在本例中,它不是一个通用引用,因此引用的折叠方式略有不同:
  • 如果T通过值传递(例如int)-〉T&&
  • 如果T是左值参考(例如int&)-〉T&
  • 如果T是另一种类型的参考(例如int&&)-〉T&&

相关问题