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
150
Other Decks in Programming
See All in Programming
マテリアルって何者?RealityKitで扱うマテリアル入門
nao_randd
0
120
カウシェで Four Keys の改善を試みた理由
ike002jp
1
140
ビカム・ア・コパイロット
ymd65536
1
170
2025-04-25 GitHub Copilot Agent ライブデモ(スクリプト)
goataka
0
130
LRパーサーはいいぞ
ydah
7
1.5k
Serving TUIs over SSH with Go
caarlos0
0
800
プロダクトエンジニアのしごと 〜 受託 × 高難度を乗り越えるOptium開発 〜
algoartis
0
250
SpringBootにおけるオブザーバビリティのなにか
irof
0
240
Live Coding: Migrating an Application to Signals
manfredsteyer
PRO
0
130
2025年のz-index設計を考える
tak_dcxi
13
5k
データと事例で振り返るDevin導入の"リアル" / The Realities of Devin Reflected in Data and Case Studies
rkaga
3
2.9k
知識0からカンファレンスやってみたらこうなった!
syossan27
5
300
Featured
See All Featured
RailsConf 2023
tenderlove
30
1.1k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
16k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
34
3k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
45
7.3k
The Art of Programming - Codeland 2020
erikaheidi
54
13k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
14
860
Fantastic passwords and where to find them - at NoRuKo
philnash
51
3.2k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
357
30k
Why Our Code Smells
bkeepers
PRO
336
57k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
160
15k
Fireside Chat
paigeccino
37
3.4k
Writing Fast Ruby
sferik
628
61k
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