这是一篇很长的帖子,所以我想在顶部写下唯一的问题:
似乎我需要为一个自定义容器实现“分配器扩展”构造函数,该容器本身不使用分配器,但将其传播到它的内部实现,它是一个变体类型,其允许的类型可能是一个容器,如 std:: map,也是一种不需要分配器的类型,比如布尔值。
独自一人,我不知道如何做到这一点。
非常感谢您的帮助!;)
“自定义容器”是一个类模板value
,它是 JSON 数据结构表示的实现。
类模板value
是一个可区分联合的薄包装器:类模板variant
(类似于 boost 变体)。此变体允许的类型表示 JSON 类型 Object、Array、String、Number Boolean 和 Null。
类模板value
有一个可变参数模板模板参数包Policies
,它基本上定义了 JSON 类型的实现方式。默认情况下,JSON 类型使用std::map
(对于 Object)、std::vector
(对于 Array)、std::string(对于 JSON 数据字符串)和一些代表剩余 JSON 类型的自定义类来实现。
中定义的类型机器用于根据给定和自身value
创建容器类型的递归类型定义。(例如,当变体类使用 std::map 或 std::vector 时,变体类不需要使用“递归包装器”来实现 JSON 容器)。也就是说,这种类型机制创建了用于表示 JSON 类型的实际类型,例如一个for Array 其equals和一个for Object 其equals 。(是的,在生成类型时实际上是不完整的)。Policies
value
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
不是-的allocator_type,value
而是仅在类型机器中使用以生成正确的实现类型。也就是说,没有嵌入allocator_type
-value
但它会影响实现类型的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 类型,即字符串的实际副本。
也许这可以通过值中的“分配器扩展”构造函数来实现,尽管它本身不使用分配器——而是需要在需要时适当地“传播”它们。
但我怎样才能做到这一点?
编辑:揭示类型生成的一部分:
“策略”是一个模板模板参数,其第一个参数是 value_type(在这种情况下value
),第二个参数是分配器类型。“策略”定义了如何根据值类型和分配器类型来实现 JSON 类型(例如数组)。
例如,对于 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;