_std::array<T,N> v
_と_T u[N]
_の基礎となるビット表現は同じですか?
言い換えれば、N*sizeof(T)
バイトを一方から他方へコピーしても安全ですか? (_reinterpret_cast
_またはmemcpy
のいずれかを使用します。)
編集:
明確にするために、強調は同じビット表現と_reinterpret_cast
_に重点を置いています。
たとえば、いくつかのT
について、いくつかの自明なコピーが可能な型N
にこれらの2つのクラスがあるとします。
_struct VecNew {
std::array<T,N> v;
};
struct VecOld {
T v[N];
};
_
そしてレガシー機能があります
_T foo(const VecOld& x);
_
表現が同じ場合、この呼び出しは安全であり、コピーを回避します。
_VecNew x;
foo(reinterpret_cast<const VecOld&>(x));
_
私は「はい」と言います(しかし、標準はそれを保証しません)。
[配列]/2によれば:
配列はaggregate([ dcl.init.aggr ])であり、Tに変換可能な型を持つ最大N個の要素でリスト初期化できます。
そして[dcl.init.aggr]:
aggregateは配列またはクラス(句[クラス])で、
ユーザー提供、明示的、または継承されたコンストラクター([class.ctor])はありません。
プライベートまたは保護された非静的データメンバー(条項[class.access])、
仮想関数なし([class.virtual])、および
仮想、プライベート、または保護された基本クラス([class.mi])はありません。
これに照らして、 "can can be list-initialized"は、クラスの先頭に他のメンバーがなく、vtableがない場合にのみ可能です。
次に、data()
は次のように指定されます。
constexpr T* data() noexcept;
戻り値:[data(), data() + size())
が有効な範囲であるようなポインター、およびdata() == addressof(front())
。
標準は基本的に「配列を返す」と言いたいが、他の実装のための扉は開いたままにしている。
他に可能な唯一の実装は、個々の要素を持つ構造です。その場合、エイリアスの問題にできます。しかし、私の見解では、このアプローチは複雑さ以外に何も追加しません。配列を構造体に展開しても何も得られません。
したがって、意味がないではないstd::array
を配列として。
しかし、抜け穴は存在します。
これはあなたの質問に直接答えることはしませんが、単に std::copy
:
T c[N];
std::array<T, N> cpp;
// from C to C++
std::copy(std::begin(c), std::end(c), std::begin(cpp));
// from C++ to C
std::copy(std::begin(cpp), std::end(cpp), std::begin(c));
T
がささいにコピー可能なタイプの場合、これはmemcpy
にコンパイルされます。そうでない場合、これは要素ごとのコピー割り当てを行い、正しくなります。いずれにせよ、これは正しいことを行い、非常に読みやすいです。手動のバイト演算は必要ありません。
std::array
が提供するメソッド data() これは、適切なサイズのcスタイルの配列との間でコピーするために使用できます。
const size_t size = 123;
int carray[size];
std::array<int,size> array;
memcpy( carray, array.data(), sizeof(int) * size );
memcpy( array.data(), carray, sizeof(int) * size );
ドキュメント で述べたように
このコンテナーは、Cスタイルの配列T [N]を唯一の非静的データメンバーとして保持する構造体と同じセマンティクスを持つ集計型です。
したがって、メモリフットプリントはCスタイルの配列と互換性があるようですが、オーバーヘッドのない適切な方法がある場合にreinterpret_cast
で「ハック」を使用する理由は明確ではありません。
data()
メソッドの要件は、次のようなポインタ_T*
_を返すことです。
[data(), data() + size())
は有効な範囲であり、data() == addressof(front())
です。
これは、data()
ポインターを介して各要素に順次アクセスできることを意味します。したがって、T
が簡単にコピーできる場合は、memcpy
を使用してsizeof(T) * size()
バイトをコピーできます。これは、配列T[size()]
との間で行われます。これは、各要素を個別にmemcpy
ingすることと同じであるためです。
ただし、_reinterpret_cast
_を使用することはできません。これは、厳密なエイリアスに違反するためです。data()
は実際にはを配列で使用する必要がないためです。 C++ 17では(_std::array
_を使用しても)配列へのポインターをその最初のメンバーへ/からポインターからキャストできないため、_reinterpret_cast
_に配列が含まれていることを保証します(_std::launder
_)。
array
は、それをインスタンス化する基礎となる型についてはあまり要求しません。
anyを使用して、memcpy
またはreinterpret_cast
コピーを行うには、それをインスタンス化した型を簡単にコピーできるようにする必要があります。これらのアイテムをarray
に保存しても、memcpy
(など)が単純にコピー可能な型でのみ機能するという要件には影響しません。
array
は、連続するコンテナーおよび集約である必要があります。これは、要素のストレージが配列でなければならないことを意味します。標準はそれを次のように示しています:
T elems[N]; // exposition only
ただし、後で、少なくとも配列が必要であることを示唆している(§[array.overview]/4)という注記があります。
[注:メンバー変数
elems
は、array
がクラスの集合体であることを強調するために、説明のためにのみ表示されています。 名前elems
はアレイのインターフェイスの一部ではありません。 —注の終了]
【強調追加】
実際には、必須ではない特定の名前elems
だけであることに注意してください。