位于::std
命名空间的类模板通常可以由程序为用户定义的类型进行专门化。我没有发现任何关于std::allocator
的特殊规定。
那么,我是否可以为自己的类型专门化std::allocator
?如果可以,是否需要提供std::allocator
主模板的所有成员,鉴于许多成员可以由std::allocator_traits
提供(并因此在C++17中被弃用)?
考虑这个程序
#include<vector>
#include<utility>
#include<type_traits>
#include<iostream>
#include<limits>
#include<stdexcept>
struct A { };
namespace std {
template<>
struct allocator<A> {
using value_type = A;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using propagate_on_container_move_assignment = std::true_type;
allocator() = default;
template<class U>
allocator(const allocator<U>&) noexcept {}
value_type* allocate(std::size_t n) {
if(std::numeric_limits<std::size_t>::max()/sizeof(value_type) < n)
throw std::bad_array_new_length{};
std::cout << "Allocating for " << n << "\n";
return static_cast<value_type*>(::operator new(n*sizeof(value_type)));
}
void deallocate(value_type* p, std::size_t) {
::operator delete(p);
}
template<class U, class... Args>
void construct(U* p, Args&&... args) {
std::cout << "Constructing one\n";
::new((void *)p) U(std::forward<Args>(args)...);
};
template<class U>
void destroy( U* p ) {
p->~U();
}
size_type max_size() const noexcept {
return std::numeric_limits<size_type>::max()/sizeof(value_type);
}
};
}
int main() {
std::vector<A> v(2);
for(int i=0; i<6; i++) {
v.emplace_back();
}
std::cout << v.size();
}
该程序使用libc++(带有
-std=c++17 -Wall -Wextra -pedantic-errors -O2 -stdlib=libc++
的Clang编译)的输出结果为:Allocating for 2
Constructing one
Constructing one
Allocating for 4
Constructing one
Constructing one
Allocating for 8
Constructing one
Constructing one
Constructing one
Constructing one
8
使用libstdc++ (带有Clang的-std=c++17 -Wall -Wextra -pedantic-errors -O2 -stdlib=libstdc++
) 的输出是:
Allocating for 2
Allocating for 4
Constructing one
Constructing one
Allocating for 8
Constructing one
Constructing one
Constructing one
Constructing one
8
正如您所见,libstdc ++并不总是使用我提供的 construct 的重载函数。如果我删除
construct ,
destroy 或
max_size 成员,则该程序甚至无法编译,因为libstdc++会抱怨缺少这些成员,尽管它们由
std :: allocator_traits 提供。
该程序是否具有未定义的行为,因此两个标准库都是正确的,还是程序的行为被明确定义,并且需要标准库使用我的专业化?
请注意,在我的专业化中仍然留有 std :: allocator 主模板的某些成员。我是否也需要添加它们?
要精确,我省略了:
<code>using is_always_equal = std::true_type
</code>
由于我的分配器是空的,所以需要使用 std::allocator_traits
提供的功能,但这些功能本应该是 std::allocator
接口的一部分。
我还省略了 pointer
, const_pointer
, reference
, const_reference
, rebind
和 address
这些内容,它们都由 std::allocator_traits
提供,并在 C++17 中被标记为过时,因为它们本应该属于 std::allocator
接口。
如果您认为必须定义所有这些内容来匹配 std::allocator
接口,请考虑将它们添加到代码中。