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
270
return文におけるstd::moveについて
return文でstd::moveをいつ使うべきかとその理由を完全理解してください💪
onihusube
December 20, 2024
Tweet
Share
More Decks by onihusube
See All by onihusube
非侵入的モナド的操作導入ライブラリ harmony とコンセプト・CPO
onihusube
0
120
Other Decks in Programming
See All in Programming
create_tableをしただけなのに〜囚われのuuid編〜
daisukeshinoku
0
230
CSC305 Lecture 26
javiergs
PRO
0
140
useSyncExternalStoreを使いまくる
ssssota
6
1k
KubeCon + CloudNativeCon NA 2024 Overviewat Kubernetes Meetup Tokyo #68 / amsy810_k8sjp68
masayaaoyama
0
240
Итераторы в Go 1.23: зачем они нужны, как использовать, и насколько они быстрые?
lamodatech
0
440
これが俺の”自分戦略” プロセスを楽しんでいこう! - Developers CAREER Boost 2024
niftycorp
PRO
0
180
テスト自動化失敗から再挑戦しチームにオーナーシップを委譲した話/STAC2024 macho
ma_cho29
1
1.3k
たのしいparse.y
ydah
3
120
RWC 2024 DICOM & ISO/IEC 2022
m_seki
0
200
複雑な仕様に立ち向かうアーキテクチャ
myohei
0
170
tidymodelsによるtidyな生存時間解析 / Japan.R2024
dropout009
1
660
Semantic Kernelのネイティブプラグインで知識拡張をしてみる
tomokusaba
0
180
Featured
See All Featured
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
26
1.9k
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7k
Optimising Largest Contentful Paint
csswizardry
33
3k
The Art of Programming - Codeland 2020
erikaheidi
53
13k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
17
2.2k
Fashionably flexible responsive web design (full day workshop)
malarkey
405
65k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
132
33k
Producing Creativity
orderedlist
PRO
341
39k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
232
17k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
29k
Building a Scalable Design System with Sketch
lauravandoore
460
33k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
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