引用初始化 - 临时绑定到 return 值
Reference initialization - temporary bound to return value
在 cppreference.com (Lifetime of a temporary) 的一篇关于引用初始化的文章中,它说:
a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of the return expression. Such function always returns a dangling reference.
此摘录解决了通过绑定对它的引用来延长临时对象的生命周期的异常。他们到底是什么意思?我想过
#include <iostream>
int&& func()
{
return 42;
}
int main()
{
int&& foo = func();
std::cout << foo << std::endl;
return 0;
}
所以 foo
应该引用临时 42。根据摘录,这应该是一个悬空引用 - 但它打印 42 而不是一些随机值,所以它工作得很好。
我确定我在这里弄错了,如果有人能解决我的困惑,我将不胜感激。
你的例子很好,但你的编译器不是。
临时值通常是文字值、函数 return 值,但也是使用语法“class_name(constructor_arguments)”传递给函数的对象。例如,在将 lambda 表达式引入 C++ 之前,为了对事物进行排序,我们可以定义一些带有重载 operator()
的结构 X
,然后像这样进行调用:
std::sort(v.begin(), v.end(), X());
在这种情况下,您希望使用 X()
构造的临时对象的生命周期将在结束指令的分号处结束。
如果你调用一个需要常量引用的函数,比如 void f(const int & n)
,带有一个临时变量,例如f(2)
,编译器创建一个临时文件 int
,用 2
初始化它,并将对该临时文件的引用传递给函数。您希望此临时文件以 f(2);
.
中的分号结束其生命周期
现在考虑一下:
int && ref = 2;
std::cout << ref;
此代码完全有效。但是请注意,这里编译器还创建了一个 int
类型的临时对象,并用 2
初始化了它。这是 ref
绑定到的这个临时文件。但是,如果临时文件的生命周期仅限于创建它的指令,并以标记指令结束的分号结束,则下一条指令将是一场灾难,因为 cout
将使用悬空引用。因此,像上面那样引用临时对象是相当不切实际的。这就是需要“延长临时生命周期”的原因。我怀疑编译器在看到类似 int && ref = 2
的内容后被允许将其转换为类似
的内容
int tmp = 2;
int && ref = std::move(tmp);
std::cout << ref; // equivalent to std::cout << tmp;
如果没有生命周期扩展,这可能看起来像这样:
{
int tmp = 2;
int && ref = std::move(tmp);
}
std::cout << ref; // what is ref?
在 return 语句中做这样的把戏是没有意义的。没有合理、安全的方法来延长函数局部对象的生命周期。
顺便说一句。大多数现代编译器发出警告并减少您的功能
int&& func()
{
return 42;
}
至
int&& func()
{
return nullptr;
}
在任何试图取消引用 return 值时立即出现段错误。
在 cppreference.com (Lifetime of a temporary) 的一篇关于引用初始化的文章中,它说:
a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of the return expression. Such function always returns a dangling reference.
此摘录解决了通过绑定对它的引用来延长临时对象的生命周期的异常。他们到底是什么意思?我想过
#include <iostream>
int&& func()
{
return 42;
}
int main()
{
int&& foo = func();
std::cout << foo << std::endl;
return 0;
}
所以 foo
应该引用临时 42。根据摘录,这应该是一个悬空引用 - 但它打印 42 而不是一些随机值,所以它工作得很好。
我确定我在这里弄错了,如果有人能解决我的困惑,我将不胜感激。
你的例子很好,但你的编译器不是。
临时值通常是文字值、函数 return 值,但也是使用语法“class_name(constructor_arguments)”传递给函数的对象。例如,在将 lambda 表达式引入 C++ 之前,为了对事物进行排序,我们可以定义一些带有重载 operator()
的结构 X
,然后像这样进行调用:
std::sort(v.begin(), v.end(), X());
在这种情况下,您希望使用 X()
构造的临时对象的生命周期将在结束指令的分号处结束。
如果你调用一个需要常量引用的函数,比如 void f(const int & n)
,带有一个临时变量,例如f(2)
,编译器创建一个临时文件 int
,用 2
初始化它,并将对该临时文件的引用传递给函数。您希望此临时文件以 f(2);
.
现在考虑一下:
int && ref = 2;
std::cout << ref;
此代码完全有效。但是请注意,这里编译器还创建了一个 int
类型的临时对象,并用 2
初始化了它。这是 ref
绑定到的这个临时文件。但是,如果临时文件的生命周期仅限于创建它的指令,并以标记指令结束的分号结束,则下一条指令将是一场灾难,因为 cout
将使用悬空引用。因此,像上面那样引用临时对象是相当不切实际的。这就是需要“延长临时生命周期”的原因。我怀疑编译器在看到类似 int && ref = 2
的内容后被允许将其转换为类似
int tmp = 2;
int && ref = std::move(tmp);
std::cout << ref; // equivalent to std::cout << tmp;
如果没有生命周期扩展,这可能看起来像这样:
{
int tmp = 2;
int && ref = std::move(tmp);
}
std::cout << ref; // what is ref?
在 return 语句中做这样的把戏是没有意义的。没有合理、安全的方法来延长函数局部对象的生命周期。
顺便说一句。大多数现代编译器发出警告并减少您的功能
int&& func()
{
return 42;
}
至
int&& func()
{
return nullptr;
}
在任何试图取消引用 return 值时立即出现段错误。