【问题标题】:Why doesn't emplace/_front/_back return a reference?为什么 emplace_front/back 不返回引用?
【发布时间】:2014-06-26 14:21:28
【问题描述】:

在使用std::vectors、std::lists(或其他 STL 容器)时,我碰巧经常写这个,因为代码简短(而不是每次都显式地输入vec[index])和内存分配效率(避免复制/move),我想我不是唯一这样做的人:

std::vector<A> vec;
vec.emplace_back();
A &element = vec[vec.size()-1];
element.prop = "value";

为什么 STL 容器的 emplaceemplace_backemplace_front 方法不返回 T&amp; 它允许人们简单地编写这个而不是使用阴暗的 @987654329 @:

std::vector<A> vec;
A &element = vec.emplace_back();
element.prop = "value";

【问题讨论】:

  • 这里有vector::back()...
  • 我认为emplace_back 具有void 返回类型最可能的理由是与其他值插入方法对称,例如push_back。编写一个执行emplace_back 并返回您正在寻找的参考的实用函数并不难。
  • @Deduplicator。好问题,不知道。我想是遗留(和兼容性);也许从来没有想过。
  • 其他人可能也在想为什么你不能这样做:vec.emplace_back().emplace_back()
  • 我真的不知道他们为什么要这么做。他们的名字没有告诉我他们应该返回一个值。而且它不会给你带来任何效率方面的东西。

标签: c++ c++11 stl return-value


【解决方案1】:

这已在C++17 中修复。 你的例子

std::vector<A> vec;
A &element = vec.emplace_back();
element.prop = "value";

是有效的 C++17 代码。

【讨论】:

  • 为什么是引用而不是迭代器?
  • @akim 因为在 std::vector(-like) 容器的情况下,emplace_font/back() 将始终返回 front()/back(),并且返回一个迭代器将需要额外的间接访问放置对象。
  • "fixed" 在旁观者的眼中,假设我们认为缺少的是错误而不是功能 ;-) 但我想这应该足够无害,因为它应该是微不足道的该引用未使用并对其进行优化。无论如何都要为信息 +1。
【解决方案2】:

您有访问这些对象的成员方法,因为您知道它们被插入到哪里。即front()back()

其他一些函数(例如map::insert)会返回一个迭代器,因为你不知道如何在恒定时间内访问插入的元素。对于emplace,你知道。

不返回任何内容的另一个原因可能是性能(大多数情况下您不会使用返回的值)。而在 C++ 中,您为所用的付费。

【讨论】:

  • 编译器优化不使用返回值的emplace_back 的机会很高(因为大多数函数被内联的机会很高,编译器可以简单地删除死代码与退货有关)。另一方面,编译器优化 emplace_back() 后跟 back() 的机会非常低,因为它需要跨两个不同的函数进行优化并“查看”动态分配给存储并能够跟踪size 成员的值,然后看到刚刚插入的元素被访问了。
  • emplace 流有一个通常没有内衬的“增长向量”慢速路径,这使得这两个函数的优化机会变得更加复杂。接近于零,即使通常不会出现增长路径。
【解决方案3】:

你不需要它。写这个:

template<class C, class...Args>
auto emplace_back(C& c, Args&&...args)->decltype(c.back()){
  c.emplace_back(std::forward<Args>(args)...);
  return c.back();
}

并且您无需修改​​容器接口即可获得所需的语义。

只是:

emplace_back(vec).prop = "foo";

【讨论】:

    【解决方案4】:

    选择此签名的两个参数:

    • API 对称。这些 API 与队列实现的 pop_back、pop_front 和 push 和 pop 是对称的。这些函数(pop 函数)有这样一种情况,即元素可能在出现异常时丢失(即元素从集合中删除,但在返回之前发生异常(例如,如果对象的构造函数可以抛出)。

      通过将此功能(读取元素和弹出元素)实现为两个独立的功能,两者都可以事务性实现。

    • SRP。这是一个很好的设计指南,如果你描述一个函数的行为并且需要使用“and”这个词,那么你已经破坏了 SRP 并且可能应该将它一分为二(即一个函数“在末尾附加一个元素 and 返回对它的引用”可能应该写成两个函数“在末尾添加元素”和“在末尾返回元素”——这两个函数至少可以为客户端代码提供弱异常保证)。

    我不确定这些标准是否适用于设计,但我记得在异常安全讲座中给出的异常保证论点。

    【讨论】:

    • 请注意,在共享数据结构中,“在末尾添加元素”和“在末尾返回元素”两个动作的顺序与单个动作的顺序不同动作“在末尾附加一个元素并返回对该新添加元素的引用”。
    【解决方案5】:

    在 c++ 11 中你可以使用:

    m.emplace(val1, val2).first
    

    获取对返回的迭代器的引用,然后:

    m.emplace(val1, val2).first->first
    

    m.emplace(val1, val2).first->second
    

    访问地图的k和v

    :)

    【讨论】:

      猜你喜欢
      • 2021-11-19
      • 1970-01-01
      • 2014-05-19
      • 2020-06-20
      • 2017-06-29
      • 2018-06-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多