如何为作用域分配器模型启用自定义容器
How to Enable a Custom Container for the Scoped Allocator Model
这是一个很长的帖子,所以我想把唯一的问题写在顶部:
似乎我需要实现自定义容器的"分配器扩展"构造函数,它本身不使用分配器,但将其传播到其内部实现,这是一种变体类型,其允许的类型可能是像std::map这样的容器,但也是一种不需要分配器的类型,例如布尔值。
独自一人,我不知道如何完成这件事。
非常感谢帮助!;)
"自定义容器"是一个类模板value
,它是JSON数据结构表示的实现。
类模板value
是区分联合的薄包装:类模板variant
(类似于boost变体)。此变体允许的类型表示JSON类型:对象、数组、字符串、数字、布尔值和Null。
类模板value
有一个可变模板模板参数包Policies
,它基本上定义了JSON类型是如何实现的。默认情况下,JSON类型是用std::map
(用于对象),std::vector
(用于数组),std::string(用于JSON数据字符串)和一些代表其余JSON类型的自定义类实现的。
在value
中定义的类型机制用于根据给定的Policies
和value
本身为容器类型创建递归类型定义。(例如,当变体类使用std::map或std::vector时,它不需要使用"递归包装器"来实现JSON容器)。也就是说,这种类型机制创建了用于表示JSON类型的实际类型,例如,std::vector
用于数组,其value_type
等于value
, std::map
用于对象,其mapped_type
等于value
。(是的,在生成类型时,value
实际上是不完整的)。
类模板value
基本上看起来是这样的(大大简化):
template <template <typename, typename> class... Policies>
class value
{
typedef json::Null null_type;
typedef json::Boolean boolean_type;
typedef typename <typegenerator>::type float_number_type;
typedef typename <typegenerator>::type integral_number_type;
typedef typename <typegenerator>::type string_type;
typedef typename <typegenerator>::type object_type;
typedef typename <typegenerator>::type array_type;
typedef variant<
null_type
, boolean_type
, float_number_type
, integral_number_type
, string_type
, object_type
, array_type
> variant_type;
public:
...
private:
variant_type value_;
};
value
实现了常见的问题,例如构造函数、赋值、访问器、比较器等。它还实现了转发构造函数,以便可以用参数列表构造变体的特定实现类型。
typegenerator基本上会找到相关的实现策略并使用它,除非它找不到,然后它使用默认的实现策略(这里没有详细说明,但如果有不清楚的地方请询问)。
例如array_type变为:std::vector<value, std::allocator<value>>
object_type变成std::map<std::string, value, std::less<std::string>, std::allocator<std::pair<const std::string, value>>>
到目前为止,这是正常工作的。
现在,我们的想法是允许用户指定一个自定义分配器,它用于"容器"中的所有分配和所有结构,即value
。例如,竞技场分配器。
为此,我将value
的模板参数扩展如下:
template <
typename A = std::allocator<void>,
template <typename, typename> class... Policies
>
class value ...
并且还调整了类型机制,以便在适当的时候使用scoped_allocator_adaptor。
注意,模板参数A
不是 value
的 allocator_type,而只是在类型机制中使用,以便生成适当的实现类型。也就是说,在value
中没有嵌入allocator_type
——但是它影响了实现类型的allocator_type。
现在,当使用有状态的自定义分配器时,这只能工作一半。更准确地说,它可以工作——除了作用域分配器的传播不能正确发生。例如:
假设有一个有状态的自定义分配器,其属性为id
(一个整数)。它不能被默认构造。
typedef test::custom_allocator<void> allocator_t;
typedef json::value<allocator_t> Value;
typedef typename Value::string_type String;
typedef Value::array_type Array;
allocator_t a1(1);
allocator_t a2(2);
// Create an Array using allocator a1:
Array array1(a1);
EXPECT_EQ(a1, array1.get_allocator());
// Create a value whose impl-type is a String which uses allocator a2:
Value v1("abc",a2);
// Insert via copy-ctor:
array1.push_back(v1);
// We expect, array1 used allocator a1 in order to construct internal copy of value v1 (containing a string):
EXPECT_EQ(a1, array1.back().get<String>().get_allocator());
--> FAILS !!
原因似乎是,array1不会通过值v1的拷贝将它的分配器成员(a1)传播到它当前的imp类型,即string的实际拷贝。
也许这可以通过值上的"分配器扩展"构造函数来实现,尽管它本身不使用分配器——而是需要在需要时适当地"传播"它们。
但是我怎么才能做到这一点呢?
编辑:显示部分类型生成:
"Policy"是一个模板模板参数,其第一个参数是value_type(在本例中为value
),第二个参数是分配器类型。"策略"定义了JSON类型(例如Array)应该如何根据值类型和分配器类型来实现。
例如,对于JSON数组:
template <typename Value, typename Allocator>
struct default_array_policy : array_tag
{
private:
typedef Value value_type;
typedef typename Allocator::template rebind<value_type>::other value_type_allocator;
typedef GetScopedAllocator<value_type_allocator> allocator_type;
public:
typedef std::vector<value_type, allocator_type> type;
};
其中GetScopedAllocator
定义为:
template <typename Allocator>
using GetScopedAllocator = typename std::conditional<
std::is_empty<Allocator>::value,
Allocator,
std::scoped_allocator_adaptor<Allocator>
>::type;
决定是否将分配器传递给子元素的逻辑称为uses-allocator构造,在标准中,参见20.6.7 [allocator.uses]。
有两个使用uses-allocator协议的标准组件:std::tuple
和std::scoped_allocator_adaptor
,您也可以编写用户定义的分配器来使用uses-allocator协议(但通常使用scoped_allocator_adaptor
来为现有的分配器添加对该协议的支持更容易)
如果你在value
内部使用scoped_allocator_adaptor
,那么你需要做的就是确保value
支持use -allocator构造,这是由std::uses_allocator<value, Alloc>
trait指定的。如果定义了value::allocator_type
并且std::is_convertible<value::allocator_type, Alloc>
为真,那么该特性将自动为真。如果value::allocator_type
不存在,你可以将该特性专门化为真(这就是std::promise
和std::packaged_task
所做的):
namespace std
{
template<typename A, typename... P, typename A2>
struct uses_allocator<value<A, P...>, A2>
: is_convertible<A, A2>
{ };
}
这意味着当一个value
被一个支持use -allocator构造的类型构造时,它会尝试将分配器传递给value
构造函数,所以你还需要添加分配器扩展的构造函数,这样它就可以被传递了。
让它按你想要的方式工作:
// Insert via copy-ctor: array1.push_back(v1);
custom_allocator
模板必须支持use -allocator结构,或者您必须将其包装以便Value::array_type::allocator_type
是scoped_allocator_adaptor<custom_allocator<Value>>
,我无法从您的问题中判断这是否正确。
当然,标准库实现必须支持作用域分配器,你使用什么编译器?我只熟悉GCC在这个领域的状态,GCC 4.7只支持std::vector
。对于GCC 4.8,我也添加了对forward_list
的支持。我希望剩余的容器都将在GCC 4.9中完成。
注意:您的类型还应该对所有与分配器相关的操作使用std::allocator_traits
,而不是直接调用分配器类型上的成员函数。
是的,当类型生成时,value实际上是不完整的
在实例化标准模板组件时使用不完整类型作为模板参数是未定义的行为,除非另有指定,参见17.6.4.8 [res.on.functions]。
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 如何将点击的信号和插槽添加到qt中的自定义按钮中
- C++自定义比较函数
- 如何比较自定义类的std::变体
- std::设置自定义比较器
- 如何正确实现和访问运算符的各种自定义枚举器
- flutter:即使shouldRepaint()返回true,自定义画家也不会重新绘制
- 自定义先决条件对移动分配运算符有效吗
- 使用VS Code和CMake Tools运行自定义命令
- 如何创建从Maya(或类似程序)到虚幻引擎的自定义数据导出插件
- std::ranges::elements_view,用于自定义类似元组的数据
- 跟随整数索引列表的自定义类迭代器
- 参数化自定义CMake工具链
- 如何在自定义类中启用'auto loops'?
- '_HAS_CXX17'宏是否可用于自定义项目标头以启用C++17 语言集功能?
- 我如何自定义fprintf以启用禁用
- 自定义汇编标志以启用特定功能
- C++ - 如何在自定义模板化数据容器中的迭代器上使用 advance() 启用 ADL
- 启用-std=c++0x时,自定义分配器的编译问题
- 如何为作用域分配器模型启用自定义容器