Os modelos de classe no ::std
namespace geralmente podem ser especializados por programas para tipos definidos pelo usuário. Não encontrei nenhuma exceção a esta regra para std::allocator
.
Então, posso me especializar std::allocator
para meus próprios tipos? E se me for permitido, preciso fornecer todos os membros do std::allocator
modelo primário de, já que muitos deles podem ser fornecidos por std::allocator_traits
(e, consequentemente, obsoletos no C ++ 17)?
Considere este programa
#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();
}
A saída deste programa com libc ++ (Clang with -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
e a saída com libstdc ++ (Clang with -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
Como você pode ver libstdc ++ nem sempre honrar a sobrecarga do construct
que eu forneci e se eu remover os construct
, destroy
ou max_size
membros, em seguida, o programa nem sequer compilar com libstdc ++ reclamando sobre esses membros desaparecidos, embora eles são fornecidos pela std::allocator_traits
.
O programa tem um comportamento indefinido e, portanto, as duas bibliotecas padrão estão corretas ou o comportamento do programa é bem definido e a biblioteca padrão necessária para usar minha especialização?
Note que existem alguns membros do std::allocator
modelo principal que ainda deixei de fora na minha especialização. Preciso adicioná-los também?
Para ser preciso, eu deixei de fora
using is_always_equal = std::true_type
que é fornecido pelo fato de std::allocator_traits
meu alocador estar vazio, mas faria parte da std::allocator
interface de.
Também deixado de fora pointer
, const_pointer
, reference
, const_reference
, rebind
e address
, os quais são fornecidos por std::allocator_traits
e preterido em C ++ para 17 std::allocator
de interface.
Se você acha que é necessário definir todos esses itens para corresponder std::allocator
à interface, considere-os adicionados ao código.