Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
return文におけるstd::moveについて
Search
onihusube
December 20, 2024
Programming
1
1.8k
return文におけるstd::moveについて
return文でstd::moveをいつ使うべきかとその理由を完全理解してください💪
onihusube
December 20, 2024
Tweet
Share
More Decks by onihusube
See All by onihusube
非侵入的モナド的操作導入ライブラリ harmony とコンセプト・CPO
onihusube
0
160
Other Decks in Programming
See All in Programming
C++20 射影変換
faithandbrave
0
500
ReadMoreTextView
fornewid
1
450
ktr0731/go-mcpでMCPサーバー作ってみた
takak2166
0
170
データの民主化を支える、透明性のあるデータ利活用への挑戦 2025-06-25 Database Engineering Meetup#7
y_ken
0
270
生成AIコーディングとの向き合い方、AIと共創するという考え方 / How to deal with generative AI coding and the concept of co-creating with AI
seike460
PRO
1
320
型付きアクターモデルがもたらす分散シミュレーションの未来
piyo7
0
800
Elixir で IoT 開発、 Nerves なら簡単にできる!?
pojiro
1
150
deno-redisの紹介とJSRパッケージの運用について (toranoana.deno #21)
uki00a
0
130
統一感のある Go コードを生成 AI の力で手にいれる
otakakot
0
3k
「ElixirでIoT!!」のこれまでとこれから
takasehideki
0
370
iOSアプリ開発で 関数型プログラミングを実現する The Composable Architectureの紹介
yimajo
2
210
第9回 情シス転職ミートアップ 株式会社IVRy(アイブリー)の紹介
ivry_presentationmaterials
1
190
Featured
See All Featured
Facilitating Awesome Meetings
lara
54
6.4k
Automating Front-end Workflow
addyosmani
1370
200k
Stop Working from a Prison Cell
hatefulcrawdad
270
20k
Being A Developer After 40
akosma
90
590k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
233
17k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
281
13k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
45
7.4k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
10
920
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
15
1.5k
Become a Pro
speakerdeck
PRO
28
5.4k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
107
19k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
2.8k
Transcript
return文における std::move()について いつstd::moveするべきか?
自己紹介 • C++の沼に嵌って抜け出せない人です • C++が好きです • この名前だと、CEDECでC++の発表を2回くらいしています • インターネットだとキノコのアイコンで活動しています •
物理世界では、株式会社T2という自動運転の会社でC++書いて ます • C++プログラマ積極募集中です! 2
背景 • 以前に書いた本の売り文句に「return文でいつstd::move()をす るべきか完全理解できる!」のようなことを書いた • しかし、本文には明確にその回答を書いていなかった • ずっと気になっていたが、いい機会なのでここで回答を置いて おくことに 以前書いた本
3
目的 • return文でいつstd::move()を書くべきかその理由を完全理解す る • C++17以降を対象とします 4
return文とstd::move • とても典型的には次のようなコードにおいて、return文での std::move()の使用をコンパイラに怒られる auto f() -> std::vector<int> { std::vector
vec = {1, 2, 3, 4}; ... return std::move(vec); // 警告 } g++13.2: warning: moving a local object in a return statement prevents copy elision [-Wpessimizing-move] clang 17.0.1: warning: moving a local object in a return statement prevents copy elision [- Wpessimizing-move] 5
return文とstd::move • std::move()を外すととりあえず何も言われなくなる • でも… • 本当にムーブされてるのか不安 • というかむしろ、std::move()を付けろと怒られた記憶もある •
人もいると思う auto f() -> std::vector<int> { std::vector vec = {1, 2, 3, 4}; ... return vec; // } 6
結果 • return文でいつstd::move()すればいいのかよくわからない • した方が良いように思えるけれど、しなくてもいいの? • とりあえずstd::move()してみてコンパイラに怒られたら外す、 という運用 • コードベースによっては非効率
7
何が分からないのか? • なぜ怒られているのか分からない • RVOと暗黙ムーブ周りの仕様は複雑すぎる • ムーブされているかはコードから見えないので起きていること が分からない • 本当にムーブされているのか?
• むしろ逆の指摘を受けたことがある場合、いつstd::move()が必 要なのか何も分からない ➢「なぜダメなのか?」と「いつ要るのか?」を理解する! 8
なぜ怒られている? • 警告メッセージにある通り、コピー省略最適化を阻害するから • ここでのコピー省略最適化とは、いわゆるNRVOのこと 9 g++13.2: warning: moving a
local object in a return statement prevents copy elision [-Wpessimizing-move] clang 17.0.1: warning: moving a local object in a return statement prevents copy elision [- Wpessimizing-move]
Named Return Value Optimization(NRVO) • 戻り値を引数の参照から返すようにするような最適化 // この関数にNRVOが適用されると auto f()
-> std::vector<int> { std::vector vec = {1, 2, 3, 4}; ... return vec; } // あたかもこう書き替えられたかのように動作する void f(std::vector& ret) { ret = std::vector{1, 2, 3, 4}; ... } 10
なぜNRVOが阻害されるのか? • NRVOは必須ではなく、あくまで許可されている最適化 • そのため、規格ではその適用範囲が指定されている • NRVOの対象となるreturn文は、そのオペランドが丁度変数名 でなければならない • かつ、その変数名はその関数ローカルの非volatile変数であり、関数引
数でもcatch節のパラメータでもない、必要がある • 戻り値型は非参照型 ➢return文のオペランドで関数呼び出ししていると、NRVOできない 11
なぜ関数呼び出しはダメなのか? • ex_func()が中で何をしてるか分からない • ex_func()に渡しているものと帰っているものが一致する保証がない • 定義が見えていればできる場合もあるかもしれないが、現在の 規格ではそこまでの解析を要求していない // こんな素性の良く分からない関数があったとして
auto ex_func(std::vector<int>&) -> std::vector<int>&&; auto f() -> std::vector<int> { std::vector vec = {1, 2, 3, 4}; // NRVO出来そうに見えますか・・・? return ex_func(vec); } 12
std::move()はライブラリ関数! • 先程のex_func()とstd::move()はNRVOの観点で見ると、コンパ イラの視点では差が無い • 規格でもstd::move()やstd::forward()をNRVOにおいて特別扱い すべしとは書いていない • おそらく実装負荷を考慮してのこと •
除外する提案は最近された(P2991R0) • 結果、return文におけるあらゆる関数呼び出しはNRVOを阻害 してしまう • 確実に行われなくする 13
ほとんどの場合std::move()は必要ない • std::move()をしなくても、暗黙ムーブされるため • 暗黙ムーブの対象は • C++17まで: ローカルの非volatile変数 • C++20以降:
+ローカルの非volatile右辺値参照 • return文のオペランドは変数名 • あるいは、()で囲まれていても良い ➢return文における関数呼び出しは暗黙ムーブも阻害する • 上記を満たしたうえで • C++20までは、return文でコピーが起こる場合、代わりにまずムーブ を試みる • C++23からは、オペランドがxvalueとして扱われる 14
暗黙ムーブのバージョンごとの対象範囲 • C++11 • 戻り値型と同じ非参照ローカル変数の暗黙ムーブ • オペランド型の右辺値を受ける変換コンストラクタへの暗黙ムーブ • C++20 •
ローカル右辺値参照の暗黙ムーブ • 変換演算子による戻り値型への変換時の暗黙ムーブ • 戻り値型コンストラクタへの暗黙ムーブ • 派生クラスから基底クラスへの変換が起こる場合の暗黙ムーブ • C++23 • 戻り値型が参照型の場合の暗黙ムーブ 15
暗黙ムーブの一例 auto ex_11(std::vector<int> vec) -> std::vector<int> { return vec; //
暗黙ムーブ、C++11から } auto ex_20(std::vector<int>&& vec) -> std::vector<int> { return vec; // 暗黙ムーブ、C++20から } auto ex_23(std::vector<int>&& vec) -> std::vector<int>&& { return vec; // 暗黙ムーブ、C++23から } 16
それでもstd::move()が必要な場合 • C++20の場合、C++23で許可された範囲のもの • 戻り値型が右辺値参照型であり、ローカルの右辺値参照をreturnする 場合 • C++23以降はこれも暗黙ムーブ対象なのでstd::move()は不要 • とはいえこの場合はあっても警告はされないはず
• std::move()そのもののような場合を除いて普通はこんなコード書かないでしょ auto f(std::vector<int>&& vec) -> std::vector<int>&& { // 戻り値型が右辺値参照型の場合のローカル右辺値参照の暗黙ムーブ return vec; // C++20まで // C++20まではstd::move()必須 return std::move(vec); // } 17
それでもstd::move()が必要な場合 • C++17までなら、C++20以降で許可された範囲のもの • 次の場合、C++17では暗黙ムーブ対象ではない • ローカル右辺値参照の暗黙ムーブ • 変換演算子による戻り値型への変換時の暗黙ムーブ •
戻り値型コンストラクタへの暗黙ムーブ • 派生クラスから基底クラスへの変換が起こる場合の暗黙ムーブ • returnしようとする値と戻り値型が異なる場合、とほぼまとめられる 18
C++17で暗黙ムーブされない例 auto f1(std::vector<int>&& vec) -> std::vector<int> { return vec; //
右辺値参照の暗黙ムーブ、C++20から } struct To { operator std::vector<int>() &&; }; auto f2(To t) -> std::vector<int> { return t; // 暗黙ムーブによる変換演算子適用、C++20から } struct V { V(std::vector<int>); }; auto f3(std::vector<int> vec) -> V { return vec; // コンストラクタ引数への暗黙ムーブ、C++20から } auto f4(Derived d) -> Base { return d; // 基底クラスへの変換時の暗黙ムーブ、C++20から } 19
それでもstd::move()が必要な場合 • 言語バージョンとは関係なく必要な場合がある • 戻り値型のコンストラクタを明示的に呼び出す場合 • 特に、2引数以上を渡そうとする場合 • return文のオペランドが変数名ではなくなるため •
同様に、return文で関数呼び出ししているとその引数は暗黙ムーブ されない • が、これはあまり驚きはなさそう auto f1(std::vector<int> vec) -> std::pair<int, std::vector<int>> { return {20, vec}; // コピーされる return {20, std::move(vec)}; // ムーブされる } 20
それでもstd::move()が必要な場合 • return文で関数呼び出ししている場合で、関数呼び出し感がな いもの ➢演算子を適用している場合 • これは特に、ポインタでも必要 auto f(std::optional<std::vector<int>> opt)
-> std::vector<int> { if (!opt) { return {}; } return *opt; // コピーされる return std::move(*opt); // ムーブされる return *std::move(opt); // ムーブされる } 21
まとめ 1. return文のオペランドで関数呼び出しをしていると、NRVO (コピー省略も)が行われなくなる • std::move()も例外ではない 2. return文においてstd::move()をしなくても、ほとんどの場合 暗黙ムーブによってムーブされている •
バージョン毎の差分がややこしい… ➢retrun文においてstd::move()は通常使用する必要は無い ➢C++20以降は特に ➢別の関数呼び出しを伴っている場合は、必要がある場合もある ➢コンストラクタ呼び出しや、*演算子など 22
PR • 株式会社T2ではC++プログラマを積極採用して います! • 自動運転の会社です • C++17でプログラミングできます!! • カジュアル面談から受け付けています!!!
• https://t2.auto/recruit/ 23