clang 的自定义分配器上的额外移动结构
Extra move constructions on custom allocator for clang
我正在尝试为 vector 编写自定义分配器。这是到目前为止的框架代码:
#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)
tldr;如果只是跟风,文案和两招来自:
- 复制到
__compressed_pair
构造器
- 进入
__libcpp_compressed_pair_imp
构造器
- 进入
__second_
会员
你打电话给vector(allocator_type const&)
:
_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&)
:
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<pointer, allocator_type>
, where we invoke the constructor __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<pointer, allocator_type, 0>(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)) {}
我正在尝试为 vector 编写自定义分配器。这是到目前为止的框架代码:
#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)
tldr;如果只是跟风,文案和两招来自:
- 复制到
__compressed_pair
构造器 - 进入
__libcpp_compressed_pair_imp
构造器 - 进入
__second_
会员
你打电话给vector(allocator_type const&)
:
_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&)
:
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<pointer, allocator_type>
, where we invoke the constructor __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<pointer, allocator_type, 0>(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)) {}