2

これは長い投稿なので、一番上に唯一の質問を書きたいと思います:

それ自体はアロケーターを使用しないカスタム コンテナーの「アロケーター拡張」コンストラクターを実装する必要があるようですが、これをバリアント型であり、許可された型が std:: map だけでなく、アロケーターを必要としない型、たとえばブール値。

一人では、これを達成する方法がわかりません。

助けていただければ幸いです。;)

「カスタム コンテナー」はvalue、JSON データ構造の表現の実装であるクラス テンプレートです。

クラス テンプレートvalueは、判別共用体の薄いラッパーです: クラス テンプレートvariant(ブースト バリアントに似ています)。このバリアントで許可される型は、JSON 型の Object、Array、String、Number Boolean、および Null を表します。

クラス テンプレートには、基本的に JSON 型の実装方法を定義valueする可変個引数テンプレート テンプレート パラメーター パックがあります。Policiesデフォルトでは、JSON 型はstd::map(オブジェクトの場合)、std::vector(配列の場合)、std::string (JSON データ文字列の場合)、および残りの JSON 型を表すいくつかのカスタム クラスで実装されます。

で定義された type-machineryvalueを使用して、指定されたおよびそれ自体に関してコンテナー型の再帰型定義を作成します。(バリアント クラスは、たとえば std::map または std::vector を使用する場合、JSON コンテナーの実装に「再帰ラッパー」を使用する必要はありません)。つまり、この型機構はJSON型を表すために使用される実際の型を作成します。(はい、型が生成される現時点では実際には不完全です)。Policiesvaluestd::vectorvalue_typevaluestd::mapmapped_typevaluevalue

クラス テンプレート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 は次のようになります。object_type は次 std::vector<value, std::allocator<value>> のようになります。
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_typevalueではなく、適切な実装型を生成するために型機械で使用されるだけであることに注意してください。つまり、組み込み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) であり、2 番目のパラメーターがアロケーターの型であるテンプレート テンプレート パラメーターです。「ポリシー」は、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;
4

1 に答える 1

2

アロケータを子要素に渡すかどうかを決定するロジックは、標準ではuses-allocator 構築と呼ばれます。20.6.7 [allocator.uses] を参照してください。

uses-allocator プロトコルを使用する 2 つの標準コンポーネントがstd::tupleありstd::scoped_allocator_adaptorますscoped_allocator_adaptor

scoped_allocator_adaptor内部で inを使用している場合value、スコープ指定されたアロケーターを機能させるために必要なことは、トレイトvalueで指定された uses-allocator 構築を確実にサポートすることだけです。が定義されていてstd::uses_allocator<value, Alloc>true の場合、その特性は自動的に true になります。が存在しない場合は、特性を true になるように特殊化できます (これが何をし、何をするかです)。value::allocator_typestd::is_convertible<value::allocator_type, Alloc>value::allocator_typestd::promisestd::packaged_task

namespace std
{
  template<typename A, typename... P, typename A2>
    struct uses_allocator<value<A, P...>, A2>
    : is_convertible<A, A2>
    { };
}

これは、valueが uses-allocator 構築をサポートする型によって構築される場合、アロケータをコンストラクタに渡そうとすることを意味するvalueため、アロケータ拡張コンストラクタを追加して渡せるようにする必要もあります。

これを希望どおりに機能させるには:

// Insert via copy-ctor:
array1.push_back(v1);

テンプレートは uses-allocator の構築をサポートしているcustom_allocator必要があります。または、それをラップしている必要があります。それが本当かどうか、あなたの質問からValue::array_type::allocator_typescoped_allocator_adaptor<custom_allocator<Value>>わかりません。

もちろん、これが機能するには、標準ライブラリの実装がスコープ付きアロケータをサポートする必要があります。どのコンパイラを使用していますか? 私は、GCC 4.7 のみがサポートしているこの分野での GCC の状況にしか精通していstd::vectorません。GCC 4.8 のサポートも追加しましたforward_list。残りのコンテナがすべて GCC 4.9 用に完成することを願っています。

std::allocator_traits: アロケーター型でメンバー関数を直接呼び出すのではなく、すべてのアロケーター関連の操作に対しても型を使用する必要があります。

はい、型が生成された時点では値は実際には不完全です

17.6.4.8 [res.on.functions] を参照してください。実装で機能する場合がありますが、必須ではありません。

于 2013-03-12T14:25:24.417 に答える