简介
说有一个容器
类,该类存储 widget
对象。
有一个迭代器类负责导航此类容器。此迭代器类( myiterator
)将const-Reference在其构造函数中的 stupt
的向量上进行,它需要在容器中的正确元素上迭代。代码可能看起来像这样:
class MyIterator
{
public:
MyIterator(
const Container& container, // Container to be navigated
const std::vector<Stuff>& vec) // Parameter which controls the behaviour of the iteration
: container(container)
, stuffVector(vec)
{}
...
private:
const Container& container;
const std::vector<Stuff>& stuffVector;
};
问题
我对 rvalue s对const引用的绑定以及寿命扩展的工作方式感到好奇的
。 vec
可以绑定到临时 rvalue ,但一旦 rvalue 的一生结束了。因此,这样的代码旨在为object mycontainer
创建一个迭代器是错误的:
MyIterator it = {myContainer, {stuff1, stuff2, stuff3}};
// Here it.stuffVector is a dangling reference!
在 cppReference 我发现:
每当引用绑定到临时对象或其子对象时,延长临时对象的寿命以匹配参考的寿命(...)
此寿命规则有以下例外:(...)
- 存在函数调用中的参考参数的临时绑定,直到包含该函数呼叫的完整表达式结束:如果函数返回了一个参考,该引用量超过了完整表达式,则将变成一个悬挂参考。
突出显示的部分的问题
使我探讨了这个问题:这件代码是否会成功获得 mycontainer
的内容的过滤版本?
std::vector<Widget> filteredWidgets;
std::copy_if (
MyIterator{myContainer, {stuff1, stuff2, stuff3}},
MyContainer.end(),
std::back_inserter(filteredWidgets),
[&](Widget w) { return ...; });
第二个论点是一个 rvalue ,因此存在创建悬而未决的参考的危险,但是我对文档的理解是, rvalue 的一生将被延长 std :: copy_if
的结尾,这没关系。它是哪一个?
注意:我知道问题和设计似乎有些奇怪,但它以我在野外发现的真实代码为灵感。我对一生延长的限制感兴趣,而不是提出不同的设计,这会使问题无关紧要。
Introduction
Say there is a Container
class which stores Widget
objects.
There is an iterator class in charge of navigating such container. This iterator class (MyIterator
) takes a const-reference to a vector of Stuff
in its constructor, which it needs to iterate over the right elements in the container. The code may look like this:
class MyIterator
{
public:
MyIterator(
const Container& container, // Container to be navigated
const std::vector<Stuff>& vec) // Parameter which controls the behaviour of the iteration
: container(container)
, stuffVector(vec)
{}
...
private:
const Container& container;
const std::vector<Stuff>& stuffVector;
};
The problem
I'm curious about the binding of rvalues to const references, and how the lifetime extensions work.
While vec
can be bound to a temporary rvalue, that will cause stuffVector
to be a dangling reference once the rvalue's lifetime is over. So code like this, aimed to create an iterator for object myContainer
is wrong:
MyIterator it = {myContainer, {stuff1, stuff2, stuff3}};
// Here it.stuffVector is a dangling reference!
Reading up on lifetime extension of temporary objects in cppreference I found:
Whenever a reference is bound to a temporary object or to a subobject thereof, the lifetime of the temporary object is extended to match the lifetime of the reference (...)
There are following exceptions to this lifetime rule: (...)
- a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if the function returns a reference, which outlives the full expression, it becomes a dangling reference.
The question
The highlighted part leads me to this question: would this piece of code succeed in getting a filtered version of the contents of myContainer
?
std::vector<Widget> filteredWidgets;
std::copy_if (
MyIterator{myContainer, {stuff1, stuff2, stuff3}},
MyContainer.end(),
std::back_inserter(filteredWidgets),
[&](Widget w) { return ...; });
The second argument is an rvalue, so there is the danger of creating a dangling reference, but my understanding of the documentation is that the rvalue's lifetime will be extended untill the end of std::copy_if
, so it would be fine. Which one is it?
NOTE: I am aware the problem and the design, as stated, might seem a bit strange, but it's inspired in real code I've found in the wild. I'm interested in the limits of the lifetime extension, not so much in coming up with different designs which would make the question irrelevant.
发布评论
评论(1)
是的,整个
std :: copy_if
呼叫是 full-expression ,临时std :: vector&lt; supp&gt;
才会仅在之后被销毁呼叫返回。这与副价值函数参数不同。如果构造函数采用
std :: vector&lt; supp&gt;
,而不是const std :: vector&lt; quatp&gt;&gt;&gt;&amp;
,那么是否将实现是否定义对象。当构造师返回时,长期生活或立即被摧毁。全表达是。没有一个具体案件(例如未评估的操作数,即时调用等)在此处适用,因此相关条件是:
copy_if
调用表达式是该语句中应用的唯一一个(在lambda主体之外)。Yes, the whole
std::copy_if
call is the full-expression and the temporarystd::vector<Stuff>
will be destroyed only after the call returns.This is different from by-value function parameters. If the constructor took a
std::vector<Stuff>
instead of aconst std::vector<Stuff>&
, then it would be implementation-defined whether the object lives that long or is destroyed immediately when the constructor returns.A full-expression is one of the expressions listed in [intro.execution]/5. None of the specific cases (such as unevaluated operands, immediate invocations, etc.) apply here and so falling through, the relevant condition is:
The
copy_if
call expression is the only one in that statement to which this applies (outside of the lambda body).