从同一个向量push_back元素是否安全?

Nei*_*irk 125 c++ reference vector push-back language-lawyer

vector<int> v;
v.push_back(1);
v.push_back(v[0]);
Run Code Online (Sandbox Code Playgroud)

如果第二个push_back导致重新分配,则向量中对第一个整数的引用将不再有效.那么这不安全吗?

vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
Run Code Online (Sandbox Code Playgroud)

这样可以安全吗?

Nat*_*ohl 31

看起来http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526解决了这个问题(或者与它非常相似的问题)作为标准中的潜在缺陷:

1)const参考采用的参数可以在执行功能时更改

例子:

鉴于std :: vector v:

v.insert(v.begin(),v [2]);

v [2]可以通过移动向量元素来改变

拟议的决议是,这不是一个缺陷:

vector :: insert(iter,value)需要工作,因为标准不允许它不起作用.

  • 我认为重点是实施负责进行重新分配.如果最初定义输入,则有责任确保定义行为.由于规范明确指出push_back进行复制,因此实现必须以牺牲执行时间为代价,在解除分配之前缓存或复制所有值.由于在这个特定问题中没有留下外部引用,因此迭代器和引用是否无效并不重要. (4认同)
  • @NeilKirk我认为这应该是一个明确的答案,Stephan T. Lavavej [在Reddit上](http://www.reddit.com/r/cpp/comments/vog1p/a_commonly_unknown_stdvector_pitfall/)也提到了它使用的基本相同参数. (3认同)
  • @ThomasMcLeod:是的,它显然可以触发重新分配。您通过插入新元素来扩展向量的大小。 (3认同)

Seb*_*edl 21

是的,它是安全的,标准的库实现跳过了箍来实现它.

我相信实施者会以某种方式将这一要求追溯到23.2/11,但我无法弄清楚如何,而且我也找不到更具体的东西.我能找到的最好的是这篇文章:

http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771

检查libc ++和libstdc ++的实现表明它们也是安全的.

  • 一些支持在这里真的有帮助. (9认同)
  • 这很有趣,我必须承认我从未考虑过这个案例,但实际上似乎很难实现.是否也适用于`vec.insert(vec.end(),vec.begin(),vec.end());`? (3认同)
  • 在您正在使用的版本中是23.2/11"除非另有说明(显式或通过根据其他函数定义函数),调用容器成员函数或将容器作为参数传递给库函数不应使迭代器无效to,或更改该容器内对象的值." ?但是`vector.push_back`会另外指定."如果新大小大于旧容量,则会导致重新分配." 和(在`reserve`)"重新分配使引用序列中元素的所有引用,指针和迭代器无效." (3认同)
  • @MatthieuM.否:表100说:"pre:i和j不是迭代器". (2认同)
  • 我现在正在推荐,因为这也是我的回忆,但需要参考. (2认同)

Rei*_*ica 13

该标准保证您的第一个例子是安全的.引用C++ 11

[sequence.reqmts]

3在表100和101中...... X表示一个序列容器类,a表示X包含类型元素的值T,... t表示左值或者右值X::value_type

16表101 ......

表达式 a.push_back(t) 返回类型 void 操作语义追加的副本t. 要求: TCopyInsertableX. 集装箱 basic_string,deque,list,vector

因此,即使它并非完全无关紧要,但实现必须保证在执行时不会使引用无效push_back.

  • @BenVoigt客户没有义务在整个通话过程中保持前提条件; 只是为了确保在通话开始时满足它. (8认同)
  • 我不知道这如何保证这是安全的. (7认同)
  • @BenVoigt这是一个好点,但我相信传递给`for_each`的仿函数不需要使迭代器无效.我不能提出`for_each`的参考,但我在一些算法上看到像"op和binary_op不会使迭代器或子范围无效"的文本. (6认同)
  • @Angew:它绝对会使`t`无效,唯一的问题是在复制之前还是之后.你的最后一句肯定是错的. (4认同)
  • @BenVoigt由于`t`符合列出的前提条件,因此保证了所描述的行为.不允许实现使前提条件无效,然后将其用作不按指定行为的借口. (4认同)
  • @barnes:我不同意.例如,您是否声称`std :: for_each`需要工作,即使该仿函数使该范围的结束迭代器无效?它怎么会发现新的有效结束迭代器?您必须确保在整个通话过程中满足前提条件. (4认同)
  • @Nawaz:我认为还有许多其他成员函数和算法别无选择,只能按照我的解释. (4认同)
  • @MarkB因为它是常用的意义,如果委员会没有以这种方式使用它,那么这意味着规范确实给予实施者在这种情况下产生UB的自由度:图书馆工作组对问题526的回应就是这个"需要工作,因为标准不允许它不工作,"这意味着他们确实使用了'先决条件'的常识. (3认同)
  • @jrok:我认为*"追加t的副本"*意味着`push_back`的行为就像参数是按值传递一样.我不确定. (2认同)
  • @Nawaz:您期待Channel9会话演示可移植代码吗? (2认同)
  • @Nawaz:该代码确实可以在 Visual C++ 上运行。他是否声称它适用于任何其他编译器/库?如果他声称可移植性,那他就“错了”。 (2认同)
  • @BenVoigt 除了不再保证此调用是安全的之外,还有什么好处?我更喜欢“先决条件”仍然是严格的先决条件,并且任何其他要求,例如算法调用的用户代码不会使范围无效,都是独立指定的。它可以简单地在全局位置完成,就像关于迭代器范围有效的事情一样。 (2认同)
  • @BenVoigt 您没有表现出进行此更改有任何好处。“要求在整个调用期间保持有效是一个非常普遍的原则”这不是 [前提](http://en.wikipedia.org/wiki/Precondition):“前提是条件或谓词在执行某些代码部分之前或正式规范中的操作之前,这必须始终为真。” (2认同)
  • @BenVoigt该标准明确规定所列的"要求"是先决条件:17.5.14/3 (2认同)
  • @BenVoigt“但它并没有说他们只需要在进入时保持”这正是它所说的,这就是委员会在对图书馆问题 526 的回应中重申的内容。“看下一段”`vector::push_back ` 和 `vector::insert` 没有 'Effects:' 子句,也没有指定为“等效于”某些代码序列,因此 17.5.1.4/4 不适用。 (2认同)
  • @BenVoigt 不,只有在调用函数时才需要满足函数的先决条件。等价代码中调用的函数的前提条件不是函数的前提条件。例如:假设函数 A 和 B 没有前提条件,而函数 C 有前提条件 x。现在假设 A 被定义为等价于 `B(); C();`。x 不会成为 A() 的先决条件。如果该前提条件不成立,那么只要 B() 为 C() 建立前提条件 x 就可以调用 A()。 (2认同)
  • 不是作为前提条件,因为作为前提条件意味着 B() 是否对其有影响并不重要。@BenVoigt (2认同)
  • @BenVoigt 我在使用该术语的含义是编写标准的人所期望的,更不用说它也是编程中常用的含义(而不仅仅是函数式编程。例如,参见 Eiffel,或者只是 [Wikipedia ](http://en.wikipedia.org/wiki/Precondition)。) (2认同)

Joh*_*åde 7

第一个例子是安全的并不明显,因为最简单的实现push_back是首先重新分配向量,如果需要,然后复制引用.

但至少它似乎对Visual Studio 2010来说是安全的.push_back当你向后推回一个元素时,它的实现会对这种情况进行特殊处理.代码结构如下:

void push_back(const _Ty& _Val)
    {   // insert element at end
    if (_Inside(_STD addressof(_Val)))
        {   // push back an element
                    ...
        }
    else
        {   // push back a non-element
                    ...
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 我想知道规范是否要求这是安全的. (8认同)
  • @Angew:这还不够.您需要传递一个在呼叫期间保持有效的引用,而这个引用不会. (4认同)
  • 根据该标准,不要求安全。然而,可以以安全的方式实施它。 (2认同)
  • @BenVoigt我会说它*必须是安全的(请参阅我的回答). (2认同)
  • @BenVoigt在传递引用时,它是有效的. (2认同)