如果用于创建std::reference_wrapper的引用超出了其范围,会发生什么?

huangapple go评论62阅读模式
英文:

What happens to a std:.reference_wrapper if the reference used to create it goes out of scope?

问题

如果用于创建std::reference_wrapper的引用超出了其作用域,std::reference_wrapper将成为悬垂引用。尝试使用它将导致未定义行为(UB)。

在你提供的代码示例中,当引用r在main函数中创建后,它引用了对象a。然后,你将r传递给fill函数,在fill函数中,rwrap被重新分配为引用mref4,这时mref4引用的是对象m,而不再是a。因此,r在main函数中的最后使用会导致UB,因为它引用的对象a在main函数结束时超出了作用域。

在第二个示例中,你创建了一系列引用,但最终仍然将rwrap重新分配为引用mref4,因此结果仍然会导致UB。

总之,这种行为是不安全的,应该避免将std::reference_wrapper用于可能会超出作用域的引用。

英文:

What happens to a std::reference_wrapper if the reference used to create it goes out of scope?
Can it still still provide access to the underlying object (which would still exist),
or would it be dangling and attempts to use it would result in UB?

I was experimenting with this code, and it compiled and run.
Was I just lucky, or is this the expected behavior?

#include <iostream>
using namespace std;


struct mytype {
    int stuff;
};

mytype m;

bool myfunc(mytype& my) { return my.stuff > 0; }

void fill(std::reference_wrapper<mytype>& rwrap) {
    std::cout << "entering fill" << std::endl;
    std::cout << "myfunc(rwrap) : " << myfunc(rwrap) << std::endl;
    rwrap = std::ref(m);
    std::cout << "myfunc(rwrap): " << myfunc(rwrap) << std::endl;
    std::cout << "fill done" << std::endl;
}

int main() {
    mytype a;
    a.stuff = 7;
    std::reference_wrapper<mytype> r = a;

    std::cout << "a.stuff: " << a.stuff << std::endl;
    std::cout << "r.stuff(pre):  " << r.get().stuff << std::endl;
    std::cout << "myfunc(r):" << myfunc(r) << std::endl;

    fill(r);

    std::cout << "r.stuff(post): " << r.get().stuff << std::endl;
    std::cout << "myfunc 4: " << myfunc(r) << std::endl;

    std::cout << "a.stuff: " << a.stuff << std::endl;
}

output:

a.stuff: 7
r.stuff(pre):  7
myfunc(r):1
entering fill
myfunc(rwrap) : 1
myfunc(rwrap): 0
fill done
r.stuff(post): 0
myfunc 4: 0
a.stuff: 7

Code at compiler explorer: https://godbolt.org/z/4eTnbvqjT

EDIT:
This worked too.
Just made it to demo to myself how it all "collapses".

void fill(std::reference_wrapper<mytype>& rwrap) {
    mytype& mref1 = m;
    mytype& mref2 = mref1;
    mytype& mref3 = mref2;
    mytype& mref4 = mref3;
    std::cout << "entering fill" << std::endl;
    std::cout << "myfunc(rwrap) : " << myfunc(rwrap) << std::endl;
    rwrap = std::ref(mref4);
    std::cout << "myfunc(rwrap): " << myfunc(rwrap) << std::endl;
    std::cout << "fill done" << std::endl;
}

答案1

得分: 1

rwrap = std::ref(m);

这行代码重新将 rwrap,也就是 r,绑定为对 m 的引用。

fill 返回时,m 超出作用域并被销毁。随后对 r 的使用会导致未定义行为。

如果用来创建它的引用超出了作用域会怎么样?

这里超出作用域并被销毁的并不是 std::ref(m)。这没问题。一旦赋值重新绑定 wrap,它从中重新绑定的引用不再有人关心。

这里导致未定义行为的并不是引用超出作用域,而是在 fill 返回时,底层对象消失不见了。

然后,您修改了代码示例,使 m 现在是一个全局对象,而不是函数内部声明的局部对象,因此它现在继续存在,并且现在已经定义明确了。推理是相同的,事实上,初始化引用包装器的引用包装器不再重要。

英文:
rwrap = std::ref(m);

This rebinds rwrap, a.k.a. r, as a reference to m.

m goes out of scope and gets destroyed when fill returns. Subsequence usage of r results in undefined behavior.

> if the reference used to create it goes out of scope?

It is not std::ref(m) that goes out of scope and gets destroyed here. That's fine and dandy. Once the assignment rebinds wrap, the reference it gets rebound from is something that nobody cares about any more.

It's not that the reference that goes out of scope that results in undefined behavior, here, but the underlying objects going poof when fill returns.

You then changed the code example so that m is now a global object, instead of declared local to the function, so it now continues to exist, and this is now well-defined. The reasoning is identical, the fact that the reference wrapper was initialized from a different reference wrapper is, still, immaterial.

huangapple
  • 本文由 发表于 2023年2月26日 22:22:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/75572622.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定