【问题标题】:Extra move constructions on custom allocator for clang用于clang的自定义分配器上的额外移动结构
【发布时间】:2017-06-22 16:06:20
【问题描述】:

我正在尝试为向量编写自定义分配器。这是到目前为止的骨架代码:

#include <iostream>
#include <vector>

struct Noisy
{
   Noisy() { std::cout << "Default ctor called" << '\n'; }
   Noisy(const Noisy&) { std::cout << "Copy ctor called" << '\n'; }
   Noisy& operator=(const Noisy&) { std::cout << "Copy assignment called" << '\n'; return *this; }

   Noisy(Noisy&&) { std::cout << "Move ctor called" << '\n'; }
   Noisy& operator=(Noisy&&) { std::cout << "Move assignment called" << '\n'; return *this; }
};

template <typename T>
struct StackAllocator : Noisy
{
   using value_type = T;
   T* allocate(std::size_t n) { return nullptr; }
   void deallocate(T* p, std::size_t n) {  }
};

int main()
{
   using alloc_t = StackAllocator<int>;
   auto alloc = alloc_t{};
   std::vector<int, alloc_t> vec(alloc);
}

在 gcc 6.3 上,这会产生我所期望的结果:(Online link)

Default ctor called
Copy ctor called

但是,在 clang 3.9 上,这会产生:(Online link)

Default ctor called
Copy ctor called
Move ctor called
Move ctor called

这是clang向量实现的源码。 (here),但我找不到任何可以解释这两个额外移动结构的东西。 (有趣的是我发现它使用压缩对来利用 EBO 的空分配器)

【问题讨论】:

    标签: c++ c++11 gcc clang++ allocator


    【解决方案1】:

    tldr;如果只是随叫随到,副本和二招来自:

    1. 复制到__compressed_pair构造函数中
    2. 进入__libcpp_compressed_pair_imp构造函数
    3. 转入__second_ 会员

    你拨打vector(allocator_type const&amp;):

        _LIBCPP_INLINE_VISIBILITY explicit vector(const allocator_type& __a)
    #if _LIBCPP_STD_VER <= 14
            _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value)
    #else
            _NOEXCEPT
    #endif
            : __base(__a)
        {
    #if _LIBCPP_DEBUG_LEVEL >= 2
            __get_db()->__insert_c(this);
    #endif
        }
    

    调用__vector_base(allocator_type const&amp;):

    template <class _Tp, class _Allocator>
    inline _LIBCPP_INLINE_VISIBILITY
    __vector_base<_Tp, _Allocator>::__vector_base(const allocator_type& __a)
        : __begin_(nullptr),
          __end_(nullptr),
          __end_cap_(nullptr, __a)
    {
    }
    

    其中__end_cap_ 是__compressed_pair&lt;pointer, allocator_type&gt;,我们调用构造函数__compressed_pair(pointer, allocator_type):

    _LIBCPP_INLINE_VISIBILITY __compressed_pair(_T1_param __t1, _T2_param __t2)
        : base(_VSTD::forward<_T1_param>(__t1), _VSTD::forward<_T2_param>(__t2)) {}
    

    现在,我们的类型都不相同,既不是空的也不是最终的,所以我们调用的基本构造函数是__libcpp_compressed_pair_imp&lt;pointer, allocator_type, 0&gt;(pointer, allocator_type):

    _LIBCPP_INLINE_VISIBILITY __libcpp_compressed_pair_imp(_T1_param __t1, _T2_param __t2)
        : __first_(_VSTD::forward<_T1_param>(__t1)), __second_(_VSTD::forward<_T2_param>(__t2)) {}
    

    【讨论】:

    • 该死!我停在压缩对上,此时我开始查看boost.org/doc/libs/1_63_0/libs/utility/doc/html/…。谢谢!这解释了两个额外的移动结构。虽然你能解释一下这条评论“现在,我们的类型都不相同,既不是空的也不是最终的,所以我们调用的基本构造函数是”
    • @skgbanga compressed_pair 的“压缩”表明它比只有两个成员稍微复杂一些,对吧?在这种情况下,我们无法压缩任何内容。
    猜你喜欢
    • 1970-01-01
    • 2014-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-16
    相关资源
    最近更新 更多