std::forward 的 RValue-reference 重载可能会导致悬空引用?

RValue-reference overload of std::forward potentially causing dangling reference?

提问人:Ruperrrt 提问时间:8/5/2021 更新时间:8/5/2021 访问量:102

问:

这个问题是以下问题的后续问题: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;
}

让我觉得已经涉及到一个悬空的引用:返回一个 prvalue,其计算初始化一个临时对象,该对象在调用时绑定到 。 (根据 cppreference,此转换“[...]生成一个表示临时对象的 xValue。但是,当通过将 prvalue 绑定到引用来触发临时具体化转换时,xvalue 表达式到底是什么?它甚至存在于代码中吗?这并不是因为这是一个价值。或者这个 prvalue 是通过将它绑定到 x 中的引用来转换为 xvalue 的?myForward<T>(t).get()inttypename std::remove_reference_t<T>&& tmyForward<decltype(myForward<T>(t).get())>(myForward<T>(t).get())myForward<T>(t).get()myForard

接下来,返回对 local to 的变量的 rvalue-reference,然后 -overload 绑定到该变量。最初,我以为这已经引入了一个悬而未决的引用,但后来我在 cppreference.com 上阅读了“引用初始化”一文中的“临时生命周期”段落。此生存期规则有“例外”:myFowardint&&myForwardint&&func()

“在函数调用中,与引用参数的临时绑定一直存在,直到包含该函数调用的完整表达式结束:如果函数返回的引用比完整表达式寿命长,则它将成为悬空引用。”

总之,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 成为悬空引用。尽管如此,我仍然可以在 中打印“42”(正确的值),即使在绑定到和调用之间的堆栈上放置一些其他值也是如此。此外,打印 in 、 in 和 in 的地址总是给出相同的值,这意味着它是同一个对象(本地 )。void func(int&& rvalue)int&& reffunc(ref)tmyForwardrefwrapperrvaluefuncmyForward

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

C++ C++11 按引用传递 完美转发 临时对象

评论

0赞 François Andrieux 8/5/2021
这不是预期的用途,因此如果它导致悬空引用等问题,我不会感到惊讶。据我所知,它只是为了帮助实现完美的转发。std::forward

答:

2赞 eerorika 8/5/2021 #1

第二次超载不是......可能导致悬空引用?

如果参数是非悬空引用,则返回的引用也将是非无悬垂的。如果参数是临时的,则引用将在完整表达式之后变得晃动。我不会说它是由 .如果你将 prvalue 传递给 ,那么你可能用错了。std::forwardstd::forward

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

正确。 是该版本中的悬空引用。ref

尽管如此,我仍然可以在void func(int&&rvalue)中打印“42”(正确的值),即使在绑定到int&&ref和调用func(ref)之间的堆栈上放置一些其他值也是如此

祝贺。您正在迈出了解未定义行为含义的第一步。

评论

1赞 François Andrieux 8/5/2021
学习未定义行为的过程与悲伤的 5 个阶段惊人地相似。
0赞 Ruperrrt 8/6/2021
我以为我错过了一些规则来定义这种行为。:D谢谢你的回答!