3

これらの特性をコンテナに持つ理由は何ですか ( https://en.cppreference.com/w/cpp/memory/allocator_traits )

propagate_on_container_copy_assignment  Alloc::propagate_on_container_copy_assignment if present, otherwise std::false_type
propagate_on_container_move_assignment  Alloc::propagate_on_container_move_assignment if present, otherwise std::false_type
propagate_on_container_swap             Alloc::propagate_on_container_swap if present, otherwise std::false_type
is_always_equal(since C++17)            Alloc::is_always_equal if present, otherwise std::is_empty<Alloc>::type

コンテナーの実装は、割り当てとスワップの実装で何らかの形で動作することを理解しています。(そして、これらのケースの処理は恐ろしいコードです。) 私はまた、move-from コンテナーを状態のままにする必要がある場合があること、resizebleまたは少なくとも最後の割り当て解除を呼び出すことができるため、アロケーターができないことも理解しています。無効のままにします。(個人的には、それは弱い議論だと思います。)

しかし、問題は、その情報が、カスタム アロケーター型自体の通常の実装とセマンティクスの一部にならないのはなぜでしょうか?

つまり、コンテナーのコピー代入はソース アロケーターのコピー代入を試みることができます。その構文上のコピー代入が実際にはコピーされない場合、それは、コンテナーがコピーしないと言っているようなものです propagate_on_container_copy_assignment

同じように、アロケーターを使用する代わりに、is_always_equal実際にアロケーターの割り当てを何も行わないようにすることができます。

(さらに、is_always_equalが true の場合operator==、アロケーターがstd::true_typeそれを通知するように戻すことができます。)

これらの特性は、通常の C++ の手段でカスタム アロケーターに与えることができるセマンティクスをオーバーライドしようとしているように見えます。これは、ジェネリック プログラミングと現在の C++ 哲学に反しているようです。

唯一の理由は、これが「古い」コンテナとのある種の下位互換性を満たすのに役立つと考えられることです。

今日、新しいコンテナーおよび/または新しい重要なアロケーターを作成する場合、アロケーターのセマンティクスに依存して、これらの特性を忘れることができますか?

私の見解では、move-from アロケーターが null ポインター状態を「割り当て解除」できる限り (これは、この特定のケースではほとんど何もしないことを意味します)、それは問題ないはずresizeです。 、それは単にアロケータがそのヒープにアクセスできなくなったことを意味します。


編集:実際 には、コンテナをこのように単純に記述できますか? 複雑さをカスタムアロケーターのセマンティクスに委任しますか?:

templata<class Allocator>
struct my_container{
  Allocator alloc_;
  ...
  my_container& operator=(my_container const& other){
    alloc_ = other.alloc_; // if allocator is_always_equal equal this is ok, if allocator shouldn't propagate on copy, Alloc::operator=(Alloc const&) simply shouldn't do anything in the first place
    ... handle copy...
    return *this;
  }
  my_container& operator=(my_container&& other){
    alloc_ = std::move(other.alloc_); // if allocator shouldn't propagate on move then Alloc::operator=(Alloc&&) simply shouldn't do anything.
    ... handle move...
    return *this;
  }
  void swap(my_container& other){
     using std::swap;
     swap(alloc, other.alloc); //again, we assume that this does the correct thing (including not actually swapping anything if that is the desired criteria. (that would be the case equivalent to `propagate_on_container_swap==std::false_type`)
     ... handle swap...
  }
}

アロケーターに対する唯一の真の要件は、移動元のアロケーターがこれを実行できる必要があるということだと思います。

my_allocator a2(std::move(a1));
a1.deallocate(nullptr, 0); // should ok, so moved-from container is destructed (without exception)
a1.allocate(n); // well defined behavior, (including possibly throwing bad_alloc).

そして、moved-from-allocator がヒープへのアクセスを失ったために、moved-from コンテナーのサイズを変更できない場合 (たとえば、特定のリソースのデフォルト アロクターがないため)、残念なことに、操作はスローされます (任意のサイズ変更として)。投げることができます)。

4

2 に答える 2