简体   繁体   中英

C++ custom allocator size argument as template parameter throws compiler error

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;
}

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 if Alloc::rebind<T>::other is valid and denotes a type; otherwise, Alloc<T, Args> if Alloc is a class template instantiation of the form Alloc<U, Args> , where Args is zero or more type arguments; otherwise, the instantiation of rebind_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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM