vector :: clear in libc ++ for trvially destructible types

时间:2015-01-28 20:39:27

标签: c++ c++11 clang llvm stdvector

如果vector<T, std::allocator<T>>::clear()可以轻易破坏,O(1)T吗?

gcc在bits/stl_vector.h调用std::_Destroybits/stl_construct.h)中的实现。这个实现通过std::is_trivially_destructible<T>上的标记调度优化了T可以轻易破坏的情况。

通过llvm(3.5.0)实现,vector::clear在每个元素上调用std::allocator<T>::destroy,然后调用析构函数。

 _LIBCPP_INLINE_VISIBILITY void destroy(pointer __p) {__p->~_Tp();}

最终是否会在libc ++中制作vector::clear() O(1)进行优化?

1 个答案:

答案 0 :(得分:5)

一般而言,符合条件的实现无法在O(1)中为具有非平凡析构函数的类型实现std::vector::clear

C ++ 11 [container.requirements.general] / 3陈述:

  

对于受本子条款影响且声明allocator_type的组件,存储在这些组件中的对象应使用allocator_traits<allocator_type>::construct函数构造,并使用allocator_traits<allocator_type>::destroy函数(20.6.8.2)进行销毁。

由于clear必须销毁size()元素,因此必须对相关分配器的destroy函数进行O(N)调用。但是,如果每个调用都没有时间完成(即什么都不做),那么最终结果可以有效地占用一个时间。

快速查看the current revision of libstdc++'s bits/stl_construct.h_Destroy的实现情况,可以看出它在使用默认分配器时只尝试执行此优化:

  /**
   * Destroy the object pointed to by a pointer type.
   */
  template<typename _Tp>
    inline void
    _Destroy(_Tp* __pointer)
    { __pointer->~_Tp(); }

  template<bool>
    struct _Destroy_aux
    {
      template<typename _ForwardIterator>
        static void
        __destroy(_ForwardIterator __first, _ForwardIterator __last)
      {
        for (; __first != __last; ++__first)
          std::_Destroy(std::__addressof(*__first));
      }
    };

  template<>
    struct _Destroy_aux<true>
    {
      template<typename _ForwardIterator>
        static void
        __destroy(_ForwardIterator, _ForwardIterator) { }
    };

  /**
   * Destroy a range of objects.  If the value_type of the object has
   * a trivial destructor, the compiler should optimize all of this
   * away, otherwise the objects' destructors must be invoked.
   */
  template<typename _ForwardIterator>
    inline void
    _Destroy(_ForwardIterator __first, _ForwardIterator __last)
    {
      typedef typename iterator_traits<_ForwardIterator>::value_type
                       _Value_type;
      std::_Destroy_aux<__has_trivial_destructor(_Value_type)>::
        __destroy(__first, __last);
    }

  /**
   * Destroy a range of objects using the supplied allocator.  For
   * nondefault allocators we do not optimize away invocation of 
   * destroy() even if _Tp has a trivial destructor.
   */

  template<typename _ForwardIterator, typename _Allocator>
    void
    _Destroy(_ForwardIterator __first, _ForwardIterator __last,
         _Allocator& __alloc)
    {
      typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
      for (; __first != __last; ++__first)
        __traits::destroy(__alloc, std::__addressof(*__first));
    }

  template<typename _ForwardIterator, typename _Tp>
    inline void
    _Destroy(_ForwardIterator __first, _ForwardIterator __last,
         allocator<_Tp>&)
    {
      _Destroy(__first, __last);
    }

但遗憾的是,由于标准允许std命名空间中依赖于用户定义类型([namespace.std] / 1)的模板专门化,因此并不能完全正确。例如,这个程序:

struct mytype {
    int value;

    mytype(int v) : value{v} {}

    operator int() const { return value; }
};

namespace std {
template <>
struct allocator<::mytype> {
    using value_type = mytype;

    allocator() = default;
    template <typename U>
    allocator(const allocator<U>&) {}

    mytype* allocate(std::size_t n) {
        auto result = ::operator new(n * sizeof(mytype));
        if (!result) throw bad_alloc();
        return static_cast<mytype*>(result);
    }

    void deallocate(mytype* ptr, std::size_t) noexcept {
        ::operator delete(ptr);
    }

    template <typename U, typename...Args>
    void construct(U* ptr, Args&&...args) {
        ::new ((void*)ptr) U(std::forward<Args>(args)...);
        std::cout << "constructed " << *ptr << '\n';
    }

    template <typename U>
    void destroy(U* ptr) noexcept {
        std::cout << "destroying " << *ptr << '\n';
        ptr->~U();
    }

    friend constexpr bool operator == (const allocator&, const allocator&) noexcept {
        return true;
    }
    friend constexpr bool operator != (const allocator&, const allocator&) noexcept {
        return false;
    }
};
} // namespace std

int main() {
    std::vector<mytype>{1,2,3};
}

应输出:

constructed 1
constructed 2
constructed 3
destroying 3
destroying 2
destroying 1

(元素破坏的顺序是未指定的,因此&#34;销毁&#34;行可以按任何顺序排列,但必须全部存在。)libstdc ++不正确&#34;优化&#34;拨打allocator<mytype>::constructallocator<mytype>::destroy的电话,但当然libc ++正确(DEMO)。