标准::转发的 RValue 引用重载可能导致悬空引用?



这个问题是以下问题的后续问题:std::forward 的第二次重载(cppreference.com 上的示例)。

StoryTeller的回答让我思考了陈述中涉及的价值类别foo(forward<decltype(forward<T>(arg).get())>(forward<T>(arg).get()));第二个重载不是

template< class T >
constexpr T&& forward( std::remove_reference_t<T>&& t ) noexcept;

可能导致引用悬空?将我的链接问题示例更改为

void func(int& lvalue)
{
std::cout << "I got an lvalue!" << std::endl;
}
void func(int&& rvalue)
{
std::cout << "I got an rvalue!" << std::endl;
}
// now according to the 2nd overload of std::forward
template <typename T>
T&& myForward(typename std::remove_reference_t<T>&& t)
{
return static_cast<T&&>(t);
}
struct foo
{
int i = 42;
int& get()& { return i; }
int get()&& { return i; }
};
// now with the inner and the outer forward as in the example on cppreference
template <typename T>
void wrapper(T&& t)
{
func(myForward<decltype(myForward<T>(t).get())>(myForward<T>(t).get()));
}
int main()
{
wrapper(foo());
return 0;
}

让我觉得已经涉及到一个悬而未决的引用:myForward<T>(t).get()返回一个intprvalue,其计算初始化一个临时对象,typename std::remove_reference_t<T>&& t在调用myForward<decltype(myForward<T>(t).get())>(myForward<T>(t).get())时绑定到该对象。 (根据 cpp 首选项,此转换"[...] 产生一个表示临时对象的 xvalue。 但是,通过将prvalue 绑定到引用来触发临时具体化转换时,xvalue 表达式究竟是什么?它甚至存在于代码中吗?这不是myForward<T>(t).get(),因为这是一个原则。或者这个 prvalue 是否通过将它绑定到myForard中的引用来转换为 xvalue?

接下来,myFoward返回一个右值引用int&&到这个变量的本地myForward,然后func()-重载的int&&绑定到该变量。最初,我认为这已经引入了一个悬而未决的引用,但后来我在 cppreference.com 上阅读了文章"引用初始化"的"临时生存期"段落。有"此生命周期规则的例外":

"函数调用

中引用参数的临时绑定一直存在,直到包含该函数调用的完整表达式结束:如果函数返回的引用比完整表达式活得更久,它就会变成悬而未决的引用。">

总之,myForward的这个临时对象一直存在到完整表达式的分号为止 - 一切都很好。 但是,我对"如果函数返回一个比完整表达式活久的引用,它就会变成一个悬空引用"的解释是"如果函数返回对该引用参数的引用"。这应该意味着改变wrapper(T&& t)这样

template <typename T>
void wrapper(T&& t)
{
// func(myForward<decltype(myForward<T>(t).get())>(myForward<T>(t).get()));
int&& ref = std::forward<decltype(std::forward<T>(t).get())>(std::forward<T>(t).get());
func(ref);
}

应导致 ref 成为悬空引用。尽管如此,我仍然可以在void func(int&& rvalue)中打印"42"(正确的值),即使在绑定到int&& ref和调用func(ref)之间的堆栈上放置一些其他值。此外,在myForward中打印t的地址,在wrapper中打印ref,在func中打印rvalue的地址总是给出相同的值,这意味着它是同一个对象(myForward的本地)。

同时,这应该意味着在使用 prvalue 调用std::forward的第二个重载时处理悬空引用,而不是在同一表达式中使用它返回的引用。还是我错过了有关此主题的某些内容?

第二个重载不是...可能导致引用悬空?

如果参数是非悬空引用,则返回的引用也将是非悬空引用。如果参数是临时的,则引用将在完整表达式之后变为 dang。我不会说这是由std::forward"引起"的.如果您将 prvalue 传递给std::forward,那么您可能用错了它。

这应该意味着像这样更改包装器(T&& t)...应导致 ref 成为悬空引用。

正确。ref是该版本中悬而未决的参考。

尽管如此,我仍然可以在void func(int&&

rvalue)中打印"42"(正确的值),即使将一些其他值放在绑定到int&&ref和调用func(ref)之间的堆栈上。

祝贺。您正处于学习未定义行为意味着什么的第一步。

最新更新