如果vector<T, std::allocator<T>>::clear()
可以轻易破坏,O(1)
会T
吗?
gcc在bits/stl_vector.h
调用std::_Destroy
(bits/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)
进行优化?
答案 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>::construct
和allocator<mytype>::destroy
的电话,但当然libc ++正确(DEMO)。