Submit Search
Effective modern c++ 5
•
Download as PPTX, PDF
•
3 likes
•
12,066 views
uchan_nos
Follow
第五回 Effective Modern C++ 勉強会の資料です Item 23
Read less
Read more
1 of 20
Download now
Downloaded 24 times
More Related Content
Effective modern c++ 5
1.
Effective Modern C++ 勉強会
#5 Item 23 内田 公太 (@uchan_nos) サイボウズ株式会社 2015/05/20
2.
アジェンダ • Move semantics
について軽く • Item 23: Understand std::move and std::forward. std::move と std::forward を理解しよう
3.
Move semantics • 実行コストの面 •
コストの高いコピーに変えて、値を「移動」させる • 所有権の面 • ポインタからポインタへ、所有権を「移動」させる std::unique_ptr<int> p1{ new int(41) }; std::unique_ptr<int> p2{ p1 }; std::unique_ptr<int> p1{ new int(41) }; std::unique_ptr<int> p2{ std::move(p1) }; ←コンパイルエラー
4.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • std::move は何もムーブしない • std::forward は何も転送しない • この2つは実行時に何もしない。 1バイトたりとも実行コードを生成しない。 • →単にキャストするだけの関数である • std::move は無条件に引数を rvalue へキャストする • std::forward は条件付きで引数を rvalue へキャストする std::move と std::forward が「何をしないか」
5.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • std::move は Universal Reference を受け取り、Rvalue Reference にキャストする • int i; move(i); → T は int& → T&& は int& → remove_reference<T>::type&& は int&& template<typename T> typename remove_reference<T>::type&& move(T&& param) { using ReturnType = typename remove_reference<T>::type&&; return static_cast<ReturnType>(param); }
6.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう template<typename T> delctype(auto) move(T&& param) { using ReturnType = remove_reference_t<T>&&; return static_cast<ReturnType>(param); } template<typename T> typename remove_reference<T>::type&& move(T&& param) { using ReturnType = typename remove_reference<T>::type&&; return static_cast<ReturnType>(param); } C++11 C++14 シンプル!
7.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • std::move はキャストしかしないのだから rvalue_cast などの方がより良かったかもしれない • 重要なのは std::move はキャストをするがムーブはしない ということ • std::move されたオブジェクトは rvalue となり、 普通はムーブの候補となる →例外もある(次ページ)
8.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • アノテーションを表すクラスを書く場合を考える • コンストラクタは std::string を取り、データメンバにコ ピーする • Item 41 を思い出したあなたは、値型を取ることにした class Annocation { public: explicit Annotation(std::string text); ... };
9.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • コンストラクタは text を変更する必要がないので、 「可能ならいつでも const を付けよう」 という由緒ある習慣に従うことにした class Annocation { public: explicit Annotation(const std::string text); ... };
10.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • コピーのコストを避けるため Item 41 に忠実に従い、 std::move を text に適用した class Annocation { public: explicit Annotation(const std::string text) : value(std::move(text)) { ... } ... private: std::string value; };
11.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • このコードは • コンパイルでき • リンクでき • 実行でき • text の内容が value にセットされる • が、 text がムーブされることはない • ムーブではなくコピーされる class Annocation { public: explicit Annotation(const std::string text) : value(std::move(text)) { ... }
12.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • std::move(text) → const std::string&& • これは std::string のムーブコンストラクタに渡せない • が、コピーコンストラクタには渡せる • const lvalue 参照は const rvalue に束縛可能なので。 • text は rvalue にキャストされるのにコピーされる! class string { public: ... string(const string& rhs); string(string&& rhs); ... };
13.
• ムーブ対象にしたいオブジェクトは const
にしない • const オブジェクトに対するムーブ要求は、静かにコ ピー操作に置き換わる • std::move は実際にムーブをしないばかりか、ムー ブができる型になることさえ保証しない • rvalue になることのみが保証される Item 23: Understand std::move and std::forward. std::move と std::forward を理解しよう 2つの教訓
14.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • std::forward の典型的な利用シナリオ: 他の関数に渡すための Universal reference を受け 取る関数テンプレート void process(const Widget& lvalArg); void process(Widget&& rvalArg); template<typename T> void logAndProcess(T&& param) { auto now = std::chrono::system_clock::now(); makeLogEntry(“calling ‘process’”, now); process(std::forward<T>(param)); }
15.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • process は 2 種類のオーバーロードがあるので、 logAndProcess に lvalue を渡したら上が、 logAndProcess に rvalue を渡したら下が それぞれ呼ばれてほしい。 void process(const Widget& lvalArg); void process(Widget&& rvalArg); Widget w; logAndProcess(w); logAndProcess(std::move(w));
16.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • しかし param は lvalue なので process(param) は lvalue なオーバーロードを呼び出してしまう • 関数の引数はすべて lvalue である! • param が rvalue な値で初期化されたときに限り rvalue にキャストされる仕組みが必要 void process(const Widget& lvalArg); void process(Widget&& rvalArg); template<typename T> void logAndProcess(T&& param) { ... process(param); }
17.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • そこで std::forward ですよ • T に param が rvalue で初期化されたかどうかがエン コードされている void logAndProcess(T&& param) std::forward<Widget&>(param) // Widget& std::forward<Widget>(param) // Widget&& Widget w; logAndProcess(w); // T is Widget& logAndProcess(std::move(w)); // T is Widget
18.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • T に param が rvalue で初期化されたかどうかがエン コードされている void process(const Widget& lvalArg); void process(Widget&& rvalArg); template<typename T> void logAndProcess(T&& param) { auto now = std::chrono::system_clock::now(); makeLogEntry(“calling ‘process’”, now); process(std::forward<T>(param)); }
19.
Item 23: Understand
std::move and std::forward. std::move と std::forward を理解しよう • std::move と std::forward をうまく使い分けよう • 技術的には std::forward さえあれば事足りるけど。 • std::move ならタイプ数は少なく、間違った型を渡す心配もない。 • さらに重要なのは、ムーブと転送は全く異なる概念であること class Widget { public: Widget(Widget&& rhs) : s(std::move(rhs.s)) class Widget { public: Widget(Widget&& rhs) : s(std::forward<std::string>(rhs.s))
20.
• std::move は
rvalue への無条件キャストを行う。 それ自身はムーブは一切行わない。 • std::forward はその引数が rvalue に束縛されてい るときのみ rvalue へのキャストを行う。 • std::move と std::forward は実行時に何もしない。 覚えておくべきこと Item 23: Understand std::move and std::forward. std::move と std::forward を理解しよう
Download