Reputation: 4095
The following code gives me expected result when I use custom allocator for my container (keeping the size sz as global variable)
#include <cstddef> /* size_t */
#include <new> /* Only for bad_alloc */
#include <vector>
using std::vector;
#include <iostream>
using std::cout;
using std::endl;
std::size_t constexpr sz = 4;
template <typename T> class StaticAllocator {
protected:
static T buf[sz];
public:
typedef T value_type;
T* allocate(std::size_t const n) {
if (n > sz) throw std::bad_alloc();
return buf;
}
void deallocate(T*, std::size_t) {}
};
template<typename T> T StaticAllocator<T>::buf[sz];
int main(void) {
std::vector<char, StaticAllocator<char> > v;
v.push_back('a');
v.push_back('b');
for (auto const& i : v) cout << i << endl;
}
where as this version of code gives me compiler error when I try to use the size as template parameter for the class
#include <cstddef> /* size_t */
#include <new> /* bad_alloc */
#include <vector>
using std::vector;
#include <iostream>
using std::cout;
using std::endl;
template<typename T, std::size_t sz> class StaticAllocator {
protected:
static T buf[sz];
public:
typedef T value_type;
T* allocate(std::size_t const n) {
if (n > sz) throw std::bad_alloc();
return buf;
}
void deallocate(T*, std::size_t) {}
};
template<typename T, std::size_t sz> T StaticAllocator<T, sz>::buf[sz];
int main(void) {
std::vector<char, StaticAllocator<char, 4> > v;
v.push_back('a');
v.push_back('b');
for (auto const& i : v) cout << i << endl;
}
Upvotes: 3
Views: 413
Reputation: 26292
To get an allocator for some type U
from an allocator for type T
, member alias template std::allocator_traits::rebind_alloc<U>
is used [allocator.traits.types]:
Alloc::rebind<T>::other
ifAlloc::rebind<T>::other
is valid and denotes a type; otherwise,Alloc<T, Args>
ifAlloc
is a class template instantiation of the formAlloc<U, Args>
, whereArgs
is zero or more type arguments; otherwise, the instantiation ofrebind_alloc
is ill-formed.
Note that Args
are type template parameters. In your allocators there is no rebind
. In the first case Alloc<U>
is used, Args
is empty. But in the second case, the second template parameter is a non-type one, it cannot be matched by Args
.
You need to manually add rebind
member struct:
template<typename T, std::size_t sz>
class StaticAllocator {
// ...
template<class U>
struct rebind {
using other = StaticAllocator<U, sz>;
};
};
Also note that your allocator is broken for some general type S
. First, buf
will be initialized by default constructing sz
objects S
. Then, it will be overwritten by newly constructing S
s at the same location before destructing the existing ones. The similar thing happens upon reallocation. This can lead to undefined behaviour. See this and this questions for some details.
The following solution was proposed in the now-deleted answer: inherit from std::allocator<T>
:
template<typename T, std::size_t sz> class StaticAllocator :
public std::allocator<T> {
// ...
};
The code compiles and runs, but... StaticAllocator::allocate
and StaticAllocator::deallocate
are not called (at least in libstdc++). The reason is that internally std::vector
always uses rebind
to get the allocator type:
using Tp_alloc_type =
typename gnu_cxx::alloc_traits<Alloc>::template rebind<Tp>::other;
rebind
is inherited from std::allocator<T>
and it returns std::allocator
instead of StaticAllocator
. That's why you still have to provide your own rebind
in StaticAllocator
.
Upvotes: 3