質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

4回答

5283閲覧

std::stringを含む構造体のコピーについて

tappe

総合スコア2

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/07/21 03:25

編集2021/07/21 03:26

C++17環境による開発です。
std::stringを含む構造体をコピーするコードとして下記を考えました。

c++

1struct Hoge{ 2 int a; 3 std::string b; 4}; 5 6int main() 7{ 8 Hoge dist{}; // コピー先 9 { 10 Hoge src{}; 11 src.a = 1; 12 src.b = "hogehoge"; 13 14 // コピー 15 std::memcpy(&dist, &src, sizeof(Hoge)); 16 } 17 18 // 結果出力 19 std::cout << dist.a << std::endl; 20 std::cout << dist.b << std::endl; 21}

実行した結果は、

1 hogehoge

となり、期待している結果です。
しかし、std::memcpyによるstd::stringを含む構造体のコピーは、コピー元が破棄された際に、コピー先の内容が不定となる危険性があるのではないかと考えています。

上記のようなstd::stringを含む構造体をstd::memcpyでコピーして問題ないのでしょうか?
不可だった場合、どのような解決策が考えられるでしょうか。
すぐに思いつく形としては、構造体Hogeにoperator=の演算子オーバーロードを追加し、メンバをひとつひとつコピーしていく形です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

言語仕様上の規則では trivially copyable の条件が満たされる型である場合に限ってバイト列のコピーがオブジェクトのコピーと同等であるということが保証されます。 逆に言えば条件が満たされないときは保証されません。

trivially copyable の条件はややこしいですし規格改定のたびに少しづつ変更されている部分がありますが、型を判定するためのメタ関数として std::is_trivially_copyable が用意されているのでこれを使えば判定は簡単です。 質問中のコードにおける Hoge について判定してみれば、結果は偽になることがわかります。

cpp

1#include <string> 2#include <iostream> 3#include <type_traits> 4 5struct Hoge{ 6 int a; 7 std::string b; 8}; 9 10int main() 11{ 12 std::cout << std::is_trivially_copyable<Hoge>::value << std::endl; 13}

この場合は Hoge 型のオブジェクトを std::memcpy でコピーすることは言語仕様上の保証がないのですべきではありません。

そして型が trivially copyable の条件を満たすときは代入演算子でコピーすることと std::memcpy でコピーすることが同じなのであえて std::memcpy を使う意味がありません。

投稿2021/07/21 07:47

SaitoAtsushi

総合スコア5466

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tappe

2021/07/21 08:15

trivially copyable の条件を満たしていない場合、std::memoryによるコピーに問題があるということは理解できました。 今回のHogeの構造の場合は、代入演算子によるコピーは問題ないという解釈でよろしいでしょうか。
SaitoAtsushi

2021/07/21 08:33

はい。 暗黙的に生成される代入演算子は全てのサブオブジェクト (基底とデータメンバ) について代入したかのように振る舞います。 https://timsong-cpp.github.io/cppwp/n3337/class.copy#28 つまり、この場合は Hoge の代入演算子はメンバ b をコピーするにあたって std::string の operator= を呼出す形でコピーします。
tappe

2021/07/27 00:19

わかりやすい、ご回答ありがとうございます。 代入演算子で実装することにします。
guest

0

ベストアンサー

危険性があるのではないか

全くその通りだと思います.ダメでしょう.
(そのように考えているのに memcpy を持ち出すに至った理由が想像つきません)


構造体Hogeにoperator=の演算子オーバーロードを追加し…

operator=の実装で何か特殊な処理をしないのであれば,
dist = src;
で終了する話ではないのでしょうか.

投稿2021/07/21 03:36

fana

総合スコア11708

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tappe

2021/07/21 05:01

やはりmemcpyを使うべきではないのですね。 質問対する的確な回答をいただいたのでベストアンサーとさせていただきます。
fana

2021/07/21 05:43 編集

std::stringのインスタンスが管理しているであろう「現在確保しているメモリの場所を示す情報や,そのメモリ領域を解放するための情報」が まるまるそのままコピーされてしまったらまずい: 大元のインスタンスと,(そのメモリイメージがそっくりそのままコピーされちゃった)もう1つのインスタンス のどちらか一方が「現在のメモリを解放」したらもう,他方が正当に動作できなくなる …だろうな,ってのは容易に想像が付く.
guest

0

すぐに思いつく形としては、構造体Hogeにoperator=の演算子オーバーロードを追加し、メンバをひとつひとつコピーしていく形です。

そのような代入演算は、(あえて削除、あるいは別な実装を行わない限り)デフォルトで存在します

投稿2021/07/21 03:37

maisumakun

総合スコア145208

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tappe

2021/07/21 05:00

私の今回のケースでは、デフォルト代入演算で問題ありませんでした。 ご回答ありがとうございました。
guest

0

std::memcpyによるstd::stringを含む構造体のコピーは、コピー元が破棄された際に、コピー先の内容が不定となる危険性があるのではないかと考えています。

検証すりゃえぇやん。

C++

1#include <iostream> 2#include <string> 3 4struct Hoge{ 5 int a; 6 std::string b; 7}; 8 9int main() 10{ 11 Hoge dist{}; // コピー先 12 { 13 Hoge* src= new Hoge(); 14 src->a = 1; 15 src->b = "hogehoge"; 16 17 // コピー 18 dist = *src; 19 // 廃棄 20 delete src; 21 } 22 std::cout << dist.a << std::endl; 23 std::cout << dist.b << std::endl; 24 25 Hoge src; 26 src.a = 1; 27 src.b = "HOGEHOGE"; 28 // コピー 29 dist = src; 30 // コピー元の書き換え 31 src.b = "PAYOPAYO"; 32 33 // 結果出力 34 std::cout << dist.a << std::endl; 35 std::cout << dist.b << std::endl; 36}

問題ないよ♪

[追記] memcpyやっちゃダメ、deep-copyせにゃならんから。

投稿2021/07/21 03:33

編集2021/07/21 03:46
episteme

総合スコア16614

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2021/07/21 03:40

> std::memcpyによるstd::stringを含む構造体のコピー の話ですから, > // コピー > dist = *src; という回答では,質問と全く違う話になりませんか?
episteme

2021/07/21 03:44

あー... 「std::memcpyによるstd::stringを含む構造体のコピーは、コピー元が破棄された際に、コピー先の内容が不定となる危険性があるのではないかと考えています。」 に対し、 「いやmemcpyなんかせんでもフツーに代入コピーで構わんよ」 と答えたかったです。 # 甘んじて低評価受ける所存。首洗って待ってますwww
fana

2021/07/21 03:53

指摘した者の責務として低評価を投じさせていただきましたぞ. (自分がC++の勉強に使った本の監修者に低評価を入れる日がくるとは)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問