18

代入できないベクトル要素の置換について、2 つの既存の質問があります。

オブジェクトが代入不可になる一般的な理由は、そのクラス定義にconstメンバーが含まれているためにoperator=削除されていることです。

std::vectorその要素型が代入可能である必要があります。実際、少なくとも GCC を使用すると、オブジェクトが代入可能でない場合、直接代入 ( ) も、要素を置換するとvec[i] = x;の組み合わせも機能しません。erase()insert()

vector::data()、要素の直接破壊、コピー コンストラクターでの配置 new を使用する次のような関数を使用して、未定義の動作を引き起こすことなく要素を置き換えることができますか?

template <typename T>
inline void replace(std::vector<T> &vec, const size_t pos, const T& src)
{
  T *p = vec.data() + pos;
  p->~T();
  new (p) T(src);
}

使用中の関数の例を以下に示します。これは GCC 4.7 でコンパイルされ、動作するようです。

struct A
{
  const int _i;
  A(const int &i):_i(i) {}
};

int main() {
  std::vector<A> vec;
  A c1(1);
  A c2(2);

  vec.push_back(c1);
  std::cout << vec[0]._i << std::endl;

  /* To replace the element in the vector
     we cannot use this: */
  //vec[0] = c2;

  /* Nor this: */
  //vec.erase(begin(vec));
  //vec.insert(begin(vec),c2);

  /* But this we can: */
  replace(vec,0,c2);
  std::cout << vec[0]._i << std::endl;

  return 0;
}
4

2 に答える 2

16

これは違法です。これは、デストラクタ呼び出しと配置newを使用してオブジェクトを所定の位置に再作成することを説明する3.8p7が、データメンバーのタイプに対する制限を指定しているためです。

3.8オブジェクトの存続期間[basic.life]

7-オブジェクトの存続期間が終了した後、オブジェクトが占有していたストレージが再利用または解放される前に、元のオブジェクトが占有していたストレージ位置に新しいオブジェクトが作成された場合、元のオブジェクトを指すポインター[。 ..]は、次の場合に新しいオブジェクトを操作するために使用できます。[...] —元のオブジェクトの型[...]に、型がconst-qualifiedまたは参照型である
非静的データメンバーが含まれていない[...]

したがって、オブジェクトにはconstデータメンバーが含まれているため、デストラクタを呼び出して新しい配置を行った後data、最初の要素を参照するために使用すると、ベクトルの内部ポインタが無効になります。賢明な読み方なら、他の要素にも同じことが当てはまると結論付けることができると思います。

これを正当化するのは、オプティマイザーがconstおよびreferenceデータメンバーがそれぞれ変更または再配置されていないと想定する権利があるということです。

struct A { const int i; int &j; };
int foo() {
    int x = 5;
    std::vector<A> v{{4, x}};
    bar(v);                      // opaque
    return v[0].i + v[0].j;      // optimised to `return 9;`
}
于 2012-10-16T09:42:12.107 に答える