33

固定長のシーケンスの標準コンテナはありますか?その長さは実行時に決定されます。できれば、各シーケンス要素のコンストラクターに引数を渡し、その引数を使用してconstメンバー(または参照)を初期化したいと思います。また、O(1)の特定のインデックスでシーケンス要素を取得したいと思います。私のすべての要件を同時に満たすことはできないように思えます。

  • 長さが固定されていることは知っstd::arrayていますが、その長さはコンパイル時に知る必要があります。
  • std::vectorは動的なサイズであり、を使用してコンストラクター引数を渡すことができますemplacereserve実際の再割り当てを回避するためにメモリを使用することはできますが、そのような再割り当てを理論的に許可するには、タイプを移動可能にする必要があります。これにより、たとえばconstメンバーが防止されます。
  • 次に、とがstd::listありstd::forward_listます。これは、移動可能なタイプを必要としませんが、サイズ変更可能であり、ランダムアクセスパターンではパフォーマンスがかなり低下します。また、各リストノードは個別に割り当てられる可能性があるため、このようなリストにはかなりのオーバーヘッドが伴う可能性があると感じています。
  • 不思議なことに、std::valarrayこれまでのところ、長さが固定されており、自動的にサイズ変更されないため、これが最善の策です。メソッドはありresizeますが、実際にそのメソッドを呼び出さない限り、タイプは移動可能である必要はありません。ここでの主な欠点は、カスタムコンストラクター引数がないことです。そのため、このアプローチではconstメンバーを初期化することはできません。

私が逃したいくつかの選択肢はありますか?すべての要件を満たすように標準コンテナーの1つを調整する方法はありますか?


編集:私がやろうとしていることのより正確なアイデアをあなたに与えるために、この例を見てください:

class A {
  void foo(unsigned n);
};

class B {
private:
  A* const a;
  const unsigned i;
public:
  B(A* aa) : a(aa), i(0) { }
  B(A* aa, unsigned ii) : a(aa), i(ii) { }
  B(const std::pair<A*, unsigned>& args) : B(args.first, args.second) { }
  B(const B&) = delete;
  B(B&&) = delete;
  B& operator=(const B&) = delete;
  B& operator=(B&&) = delete;
};

void A::foo(unsigned n) {
  // Solution using forward_list should be guaranteed to work
  std::forward_list<B> bs_list;
  for (unsigned i = n; i != 0; --i)
    bs_list.emplace_front(std::make_pair(this, i - 1));

  // Solution by Arne Mertz with single ctor argumen
  const std::vector<A*> ctor_args1(n, this);
  const std::vector<B> bs_vector(ctor_args1.begin(), ctor_args1.end());

  // Solution by Arne Mertz using intermediate creator objects
  std::vector<std::pair<A*, unsigned>> ctor_args2;
  ctor_args2.reserve(n);
  for (unsigned i = 0; i != n; ++i)
    ctor_args2.push_back(std::make_pair(this, i));
  const std::vector<B> bs_vector2(ctor_args2.begin(), ctor_args2.end());
}
4

4 に答える 4

10

理論的vectorには、必要なプロパティがあります。お気づきのとおり、要素がコピー不可および/または譲渡不可の場合、特にシーケンスの変更(empace_back、push_back、insertなど)を含む、含まれているタイプへの割り当てを行う可能性のあるアクションはサポートされません。したがって、コピー不可能な要素のベクトルを作成するには、ベクトルの作成中に各要素を作成する必要があります。

Steve Jessopが彼の答えで指摘しているように、最初にベクトルconstを定義すると、そのような変更アクションを呼び出すことさえできなくなります。もちろん、要素も変更されません。

私が正しく理解していれば、コンストラクター引数のシーケンスのみがあり、実際のオブジェクトシーケンスはありません。引数が1つだけで、含まれている型に対応するコンストラクターがある場合は、簡単です。

struct C
{
  const int i_;  
  C(int i) : i_(i) {}
};

int main()
{
  const std::vector<C> theVector { 1, 2, 3, 42 };
}

コンストラクターが明示的である場合は、最初にリストを作成するか、initializer-list内のオブジェクトを明示的に構築する必要があります。

int main()
{
  auto list = { 1, 2, 3, 4 };
  const std::vector<C> theVector (std::begin(list), std::end(list));
  const std::vector<C> anotherVector { C(1), C(44) };
}

構築されたオブジェクトごとに複数の引数がある場合は、中間の作成者オブジェクトを検討してください。

struct C
{
  const int i_;  
  C(int i, int y) : i_(i+y) {}
};

struct CCreator
{ 
  int i; int y; 
  explicit operator C() { return C(i,y); }
};

int main()
{
  const std::vector<CCreator> ctorArgs = { {1,2}, {3,42} };
  const std::vector<C> theVector { begin(ctorArgs), end(ctorArgs) };
}
于 2013-02-15T13:15:19.410 に答える
6

私はconst std::vector<T>あなたが求める特性を持っていると思います。その要素は実際にはで定義されていませんconstが、それらのconstビューを提供します。サイズは変更できません。移動可能である必要のあるメンバー関数を呼び出すことはできないTため、通常の使用ではインスタンス化されません(externクラス宣言を行った場合はインスタンス化されるため、それを行うことはできません)。

私が間違っていて、動かせないために問題が発生した場合は、代わりTに試してください。const std::deque<T>

難しいのは、ブライターを作成することです。C++ 11では、初期化リストを使用してこれを行うことができます。C++ 03ではconst vector、非定数ベクトルまたはイテレーターを取得できるその他のものから作成できます。これは必ずしもTコピー可能である必要があることを意味するわけではありませんが、それを構築できるタイプ(おそらくあなたが目的のために発明したもの)が存在する必要があります。

于 2013-02-15T13:38:55.430 に答える
3

を使用して、間接参照のレベルを追加しますstd::shared_ptr。共有ポインタは通常どおりコピーして割り当てることができますが、ポイントされているオブジェクトを変更する必要はありません。次の例に示すように、この方法では問題は発生しません。

class a
{
public:
    a(int b) : b(b) { }

    // delete assignment operator
     a& operator=(a const&) = delete;

private:
    // const member
    const int b;
};

// main
std::vector<std::shared_ptr<a>> container;

container.reserve(10);
container.push_back(std::make_shared<a>(0));
container.push_back(std::make_shared<a>(1));
container.push_back(std::make_shared<a>(2));
container.push_back(std::make_shared<a>(3));

もう1つの利点はstd::make_shared、任意の数の引数を使用してオブジェクトを作成できる関数です。


編集:

MvGが述べているように、を使用することもできますstd::unique_ptr。間接参照の使用boost::indirect_iteratorは、要素を新しいベクトルにコピーすることで削除できます。

void A::foo(unsigned n)
{
    std::vector<std::unique_ptr<B>> bs_vector;
    bs_vector.reserve(n);

    for (unsigned i = 0; i != n; ++i)
    {
        bs_vector.push_back(std::unique_ptr<B>(new B(this, i)));
    }

    typedef boost::indirect_iterator<std::vector<std::unique_ptr<B>>::iterator> it;

    // needs copy ctor for B
    const std::vector<B> bs_vector2(it(bs_vector.begin()), it(bs_vector.end()));

    // work with bs_vector2
}
于 2013-02-15T13:28:39.213 に答える
-1

私もこの問題に遭遇します。私のコードのユースケースはスレッドセーフなベクトルを提供することであり、要素番号は固定されており、原子番号です。私はここですべての素晴らしい答えを読みました。私たちも私の解決策を検討するかもしれないと思います:

を継承しstd::vector、、、、などの修飾子を非表示にするとpush_backemplace_back固定eraseサイズのベクトルが得られます。でのみ要素にアクセスして変更できますoperator []

template <typename T>
class FixedVector : protected std::vector<T> {
 public:
  using BaseType = std::vector<T>;
  FixedVector(size_t n) : BaseType(n) {}
  FixedVector(const T &val, size_t n) : BaseType(val, n) {}
  typename BaseType::reference operator[](size_t n) {
    return BaseType::operator[](n);
  }
};
于 2021-04-06T02:49:37.003 に答える