一部のメンバーが初期化されていない構造体をコピーすることは有効ですか?
未定義の動作ではないかと思いますが、その場合、初期化されていないメンバーを構造体に残すと(それらのメンバーが直接使用されない場合でも)、非常に危険になります。だから、規格にそれを許すものがあるのかしら。
たとえば、これは有効ですか?
struct Data {
int a, b;
};
int main() {
Data data;
data.a = 5;
Data data2 = data;
}
一部のメンバーが初期化されていない構造体をコピーすることは有効ですか?
未定義の動作ではないかと思いますが、その場合、初期化されていないメンバーを構造体に残すと(それらのメンバーが直接使用されない場合でも)、非常に危険になります。だから、規格にそれを許すものがあるのかしら。
たとえば、これは有効ですか?
struct Data {
int a, b;
};
int main() {
Data data;
data.a = 5;
Data data2 = data;
}
回答:
はい。初期化されていないメンバーが符号なしナロー文字タイプまたはstd::byteでない場合、暗黙的に定義されたコピーコンストラクターでこの不確定値を含む構造体をコピーすることは、同じタイプの不確定値を持つ変数をコピーするため、技術的には未定義の動作です。[dcl.init] / 12。
暗黙的に生成されたコピーコンストラクターは、unions を除いて、直接初期化のように各メンバーを個別にコピーするように定義されているため、これがここに当てはまります。[class.copy.ctor] / 4を参照してください。
これは、アクティブなCWG問題2264の対象でもあります。
ただし、実際には問題はないと思います。
100%確実にしたいstd::memcpy場合は、メンバーの値が不確定であっても、型が自明にコピー可能であれば、常に明確な動作を使用できます。
これらの問題は別として、クラスに自明なデフォルトコンストラクターが必要ない場合は、常に、構築時にクラスメンバーを指定された値で適切に初期化する必要があります。これは、デフォルトのメンバー初期化構文を使用して簡単に行うことができます。たとえば、メンバーを値で初期化します。
struct Data {
int a{}, b{};
};
int main() {
Data data;
data.a = 5;
Data data2 = data;
}
memcpy簡単にコピーできる型であっても、オブジェクト表現をのようにコピーすることは定義されていません。唯一の例外は共用体で、暗黙のコピーコンストラクターは、あたかものようにオブジェクト表現をコピーしますmemcpy。
一般に、初期化されていないデータのコピーは、トラッピング状態にある可能性があるため、未定義の動作です。このページの引用:
オブジェクト表現がオブジェクトタイプの値を表さない場合、それはトラップ表現と呼ばれます。文字型の左辺値式を介してそれを読み取る以外の方法でトラップ表現にアクセスすることは、未定義の動作です。
シグナルNaNは浮動小数点型で可能であり、プラットフォームによっては整数にトラップ表現がある場合があります。
ただし、自明にコピー可能な型の場合は、を使用memcpyしてオブジェクトの生の表現をコピーできます。オブジェクトの値は解釈されず、オブジェクト表現の生のバイトシーケンスがコピーされるので、そうすることは安全です。
unsigned char[64])についてはどうですか?構造体のバイトをUnspecified値を持つものとして扱うと、不必要に最適化が妨げられる可能性がありますが、プログラマが手動で配列に不要な値を入力する必要があると、さらに効率が低下します。
説明されているようないくつかのケースでは、C ++標準により、コンパイラーは、動作が予測可能である必要なしに、顧客が最も有用であると思う方法で構文を処理することができます。言い換えると、このような構成は「未定義の動作」を呼び出します。ただし、C ++標準では、整形式プログラムが実行を「許可」されている対象に対する管轄を明示的に放棄しているため、そのような構成が「禁止」されることを意味しているわけではありません。C ++標準の公開されたRationaleドキュメントには気づいていませんが、C89が定義するように未定義の動作を説明しているという事実は、意図された意味が似ていることを示唆します。診断する。
何かを処理するための最も効率的な方法は、ダウンストリームコードが気にしない構造の部分を書くことを含み、ダウンストリームコードが気にしないそれらを省略する多くの状況があります。プログラムが構造体のすべてのメンバーを初期化することを要求することは、何も気にしないものも含めて、不必要に効率を妨げます。
さらに、初期化されていないデータを非決定論的な方法で動作させることが最も効率的な状況がいくつかあります。たとえば、次の場合:
struct q { unsigned char dat[256]; } x,y;
void test(unsigned char *arr, int n)
{
q temp;
for (int i=0; i<n; i++)
temp.dat[arr[i]] = i;
x=temp;
y=temp;
}
下流のコードがの要素の値x.datまたはにy.datリストされていないインデックスの値をarr考慮しない場合、コードは次のように最適化される可能性があります。
void test(unsigned char *arr, int n)
{
q temp;
for (int i=0; i<n; i++)
{
int it = arr[i];
x.dat[index] = i;
y.dat[index] = i;
}
}
プログラマがのすべての要素を明示的に書き込む必要がある場合、効率を向上させることは不可能です。これにはtemp.dat、ダウンストリームが気にしない要素も含めて、それをコピーする前に記述します。
一方、データ漏洩の可能性を回避することが重要なアプリケーションもあります。このようなアプリケーションでは、ダウンストリームコードがそれを参照するかどうかに関係なく、初期化されていないストレージをコピーする試みをトラップするように装備されたバージョンのコードを用意するか、実装がストレージを保証するように実装すると便利かもしれません内容が漏えいする可能性のあるものは、ゼロ化されるか、機密情報以外のデータで上書きされます。
私が言うことができることから、C ++標準は、これらの動作のいずれも、それを強制することを正当化するために他の動作よりも十分に有用であるとは言いません。皮肉なことに、この仕様の欠如は最適化を容易にすることを目的としている可能性がありますが、プログラマーがあらゆる種類の弱い動作保証を活用できない場合、最適化は無効になります。
のすべてのメンバーはDataプリミティブ型なので、のすべてのメンバーのdata2正確な「ビットごとのコピー」を取得しdataます。したがって、の値はの値data2.bとまったく同じになりdata.bます。ただし、data.b明示的に初期化していないため、の正確な値は予測できません。これは、に割り当てられたメモリ領域のバイト値に依存しますdata。
std::memcpy。これにより、std::memcpyまたはの使用が妨げられることはありませんstd::memmove。暗黙のコピーコンストラクターの使用を防ぐだけです。