C++自定义分配器大小参数作为模板参数会引发编译器错误

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

本文关键字:参数 错误 编译器 分配器 自定义 C++      更新时间:2023-10-16

当我为容器使用自定义分配器时,以下代码给了我预期的结果(将大小 sz 保留为全局变量(

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

当我尝试将大小用作类的模板参数时,此版本的代码会给我编译器错误

#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;
}
要从类型T

的分配器获取某些类型U的分配器,请使用成员别名模板std::allocator_traits::rebind_alloc<U>[allocator.traits.types]:

Alloc::rebind<T>::otherAlloc::rebind<T>::other是否有效,并且 表示类型;否则,Alloc<T, Args>Alloc是否是形式Alloc<U, Args>的类模板实例化,其中Args是零个或多个类型参数;否则,rebind_alloc的实例化格式不正确。

请注意,Args类型模板参数。在您的分配器中没有rebind.在第一种情况下,使用Alloc<U>Args为空。但在第二种情况下,第二个模板参数是非类型参数,它不能与Args匹配。

您需要手动添加rebind成员结构:

template<typename T, std::size_t sz>
class StaticAllocator {
// ...
template<class U>
struct rebind {
using other = StaticAllocator<U, sz>;
};
};

另请注意,对于某些常规类型S,您的分配器已损坏。首先,默认情况下将初始化buf构造sz对象S。然后,在破坏现有S之前,它将被在同一位置的新构造覆盖。重新分配时也会发生类似的事情。这可能导致未定义的行为。有关一些详细信息,请参阅此和此问题。


在现已删除的答案中提出了以下解决方案:从std::allocator<T>继承:

template<typename T, std::size_t sz> class StaticAllocator : 
public std::allocator<T> {
// ...
};

代码编译并运行,但是...调用StaticAllocator::allocateStaticAllocator::deallocate(至少在libstdc++中(。原因是内部std::vector总是使用rebind来获取分配器类型:

using Tp_alloc_type = 
typename gnu_cxx::alloc_traits<Alloc>::template rebind<Tp>::other;

rebind是从std::allocator<T>继承的,它返回std::allocator而不是StaticAllocator。这就是为什么您仍然必须在StaticAllocator中提供自己的rebind