【问题标题】:uninitialized move in std::vectorstd::vector 中未初始化的移动
【发布时间】:2012-02-07 15:16:02
【问题描述】:

我用的是visual studio 2010,它的stl实现来自dinkumware

我注意到当向量的内部缓冲区增长时,会调用 _Uninitialized_move。这很像 std::uninitialized_copy。

  1. 对于标量类型,它最终将调用转发到 memmove
  2. 对于非标量类型,它将调用包含类型的复制构造函数。

我知道对于非标量类型,按位处理是不安全的,但在这种情况下我会感到困惑,因为旧对象很快就会被销毁。看起来所有类型的按位处理都是安全的,因此我们只需要在 _Uninitialized_move 之后释放原始缓冲区。这将为非平凡对象节省大量复制构造函数和析构函数。

那么,只移动非标量类型的内容是否安全?

【问题讨论】:

    标签: c++ algorithm memory memory-management stl


    【解决方案1】:

    如果您完全确定您移动的对象之间没有指向彼此的指针(包括这些对象的子对象) - 那么只移动内容就可以了。但是,除非您知道该类型的内部设计方式,否则您无法知道这一点。一个例外是,如果类型不大于指针(sizeof(Type) <= sizeof( void* )),那么在同一个容器中就不太可能有指向对象的指针,因此通常可以只移动它。

    【讨论】:

    • @Chang:用memmove() 处理标量可能会出现奇怪的问题,但不太可能。
    【解决方案2】:

    在 C++11 中,有许多特征可以知道执行位操作是否安全。

    就你而言,我认为你应该使用std::is_trivially_move_constructible<T>。这些特征是使用编译器内在函数实现的(不可移植,这就是它在标准库中的原因),但它们本身是可移植的。

    因此,代码应该类似于:

    template <typename T>
    void move_to(T* storage, T* begin, T* end) {
      if (std::is_trivially_move_constructible<T>::value) {
        memmove(storage, begin, (end - begin) * sizeof(T));
      } else {
        for (; begin != end; ++begin, ++storage) {
          new (storage) T(std::move(*begin));
        }
      }
    }
    

    并且编译器将在编译时优化if,具体取决于该类型是否可轻松移动构造,并且只留下有趣的分支。

    【讨论】:

    • 感谢您告诉我“is_trivially_move_constructible”
    【解决方案3】:

    没有。如果它们有指向彼此的指针,那么简单的按位移动将使这些指针无效。

    【讨论】:

    • 如果它们有指向彼此的指针,那么复制构造然后销毁源也会产生死指针。如果他们有指向自己的指针,那么复制构造是可以的,但按位复制不是。
    • 我感觉我发布的内容有些不对劲(在我发布之后)。我会假设在这种情况下复制构造函数必须更智能并从这些指针复制数据(或隐藏以禁止此类行为)。但更简单的解释是你所说的指向自我的指针。
    猜你喜欢
    • 2013-06-06
    • 2015-05-22
    • 2013-07-13
    • 2012-06-09
    • 1970-01-01
    • 2012-08-07
    • 1970-01-01
    • 1970-01-01
    • 2017-04-04
    相关资源
    最近更新 更多