Шаблоны классов в пространстве имен ::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 ++ (Clang с -std=c++17 -Wall -Wextra -pedantic-errors -O2 -stdlib=libc++
):
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
, которые я до сих пор не указал в своей спецификации. известной долей. Нужно ли мне также добавлять их?
Если быть точным, я пропустил
using is_always_equal = std::true_type
, который предоставляется std::allocator_traits
, так как мой распределитель пуст, но будет частью * Интерфейс 1040 *.
Я также пропустил pointer
, const_pointer
, reference
, const_reference
, rebind
и address
, все из которых предоставлены std::allocator_traits
и устарел в C ++ 17 для интерфейса std::allocator
.
Если вы считаете, что необходимо определить все это для соответствия интерфейсу std::allocator
, то, пожалуйста, рассмотрите их как добавленные в код.