SlideShare ist ein Scribd-Unternehmen logo
1 von 20
Effective Modern C++
勉強会 #5 Item 23
内田 公太 (@uchan_nos)
サイボウズ株式会社
2015/05/20
アジェンダ
• Move semantics について軽く
• Item 23: Understand std::move and std::forward.
std::move と std::forward を理解しよう
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) };
←コンパイルエラー
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 が「何をしないか」
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);
}
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
シンプル!
Item 23: Understand std::move and std::forward.
std::move と std::forward を理解しよう
• std::move はキャストしかしないのだから
rvalue_cast などの方がより良かったかもしれない
• 重要なのは
std::move はキャストをするがムーブはしない
ということ
• std::move されたオブジェクトは rvalue となり、
普通はムーブの候補となる
→例外もある(次ページ)
Item 23: Understand std::move and std::forward.
std::move と std::forward を理解しよう
• アノテーションを表すクラスを書く場合を考える
• コンストラクタは std::string を取り、データメンバにコ
ピーする
• Item 41 を思い出したあなたは、値型を取ることにした
class Annocation {
public:
explicit Annotation(std::string text);
...
};
Item 23: Understand std::move and std::forward.
std::move と std::forward を理解しよう
• コンストラクタは text を変更する必要がないので、
「可能ならいつでも const を付けよう」
という由緒ある習慣に従うことにした
class Annocation {
public:
explicit Annotation(const std::string text);
...
};
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;
};
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))
{ ... }
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);
...
};
• ムーブ対象にしたいオブジェクトは const にしない
• const オブジェクトに対するムーブ要求は、静かにコ
ピー操作に置き換わる
• std::move は実際にムーブをしないばかりか、ムー
ブができる型になることさえ保証しない
• rvalue になることのみが保証される
Item 23: Understand std::move and std::forward.
std::move と std::forward を理解しよう
2つの教訓
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));
}
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));
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);
}
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
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));
}
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))
• std::move は rvalue への無条件キャストを行う。
それ自身はムーブは一切行わない。
• std::forward はその引数が rvalue に束縛されてい
るときのみ rvalue へのキャストを行う。
• std::move と std::forward は実行時に何もしない。
覚えておくべきこと
Item 23: Understand std::move and std::forward.
std::move と std::forward を理解しよう

Weitere ähnliche Inhalte

Was ist angesagt?

組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
kikairoya
 
Constexpr 中3女子テクニック
Constexpr 中3女子テクニックConstexpr 中3女子テクニック
Constexpr 中3女子テクニック
Genya Murakami
 

Was ist angesagt? (20)

Effective Modern C++ 勉強会#7 Item 27
Effective Modern C++ 勉強会#7 Item 27Effective Modern C++ 勉強会#7 Item 27
Effective Modern C++ 勉強会#7 Item 27
 
templateとautoの型推論
templateとautoの型推論templateとautoの型推論
templateとautoの型推論
 
effective modern c++ chapeter36
effective modern c++ chapeter36effective modern c++ chapeter36
effective modern c++ chapeter36
 
Emcpp0506
Emcpp0506Emcpp0506
Emcpp0506
 
Boost.勉強会 #21 札幌「C++1zにstring_viewが導入されてうれしいので紹介します」
Boost.勉強会 #21 札幌「C++1zにstring_viewが導入されてうれしいので紹介します」Boost.勉強会 #21 札幌「C++1zにstring_viewが導入されてうれしいので紹介します」
Boost.勉強会 #21 札幌「C++1zにstring_viewが導入されてうれしいので紹介します」
 
Emcpp item31
Emcpp item31Emcpp item31
Emcpp item31
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISる
 
クロージャデザインパターン
クロージャデザインパターンクロージャデザインパターン
クロージャデザインパターン
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
社会不適合女子が社長になってみました
社会不適合女子が社長になってみました社会不適合女子が社長になってみました
社会不適合女子が社長になってみました
 
すごいConstたのしく使おう!
すごいConstたのしく使おう!すごいConstたのしく使おう!
すごいConstたのしく使おう!
 
マーク&スイープ勉強会
マーク&スイープ勉強会マーク&スイープ勉強会
マーク&スイープ勉強会
 
ELFの動的リンク
ELFの動的リンクELFの動的リンク
ELFの動的リンク
 
Javaバイトコード入門
Javaバイトコード入門Javaバイトコード入門
Javaバイトコード入門
 
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
 
Oss貢献超入門
Oss貢献超入門Oss貢献超入門
Oss貢献超入門
 
Constexpr 中3女子テクニック
Constexpr 中3女子テクニックConstexpr 中3女子テクニック
Constexpr 中3女子テクニック
 
C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)
C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)
C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)
 

Ähnlich wie Effective modern c++ 5 (6)

Emcpp item41
Emcpp item41Emcpp item41
Emcpp item41
 
Effective Modern C++ 勉強会#6 Item25
Effective Modern C++ 勉強会#6 Item25Effective Modern C++ 勉強会#6 Item25
Effective Modern C++ 勉強会#6 Item25
 
C++0x 言語の未来を語る
C++0x 言語の未来を語るC++0x 言語の未来を語る
C++0x 言語の未来を語る
 
emcjp Item 42
emcjp Item 42emcjp Item 42
emcjp Item 42
 
Hello Dark-Side C# (Part. 1)
Hello Dark-Side C# (Part. 1)Hello Dark-Side C# (Part. 1)
Hello Dark-Side C# (Part. 1)
 
Boost.Flyweight
Boost.FlyweightBoost.Flyweight
Boost.Flyweight
 

Mehr von uchan_nos

Mehr von uchan_nos (20)

MikanOSと自作CPUをUSBで接続する
MikanOSと自作CPUをUSBで接続するMikanOSと自作CPUをUSBで接続する
MikanOSと自作CPUをUSBで接続する
 
OSを手作りするという趣味と仕事
OSを手作りするという趣味と仕事OSを手作りするという趣味と仕事
OSを手作りするという趣味と仕事
 
小型安価なFPGAボードの紹介と任意波形発生器
小型安価なFPGAボードの紹介と任意波形発生器小型安価なFPGAボードの紹介と任意波形発生器
小型安価なFPGAボードの紹介と任意波形発生器
 
トランジスタ回路:エミッタ接地増幅回路
トランジスタ回路:エミッタ接地増幅回路トランジスタ回路:エミッタ接地増幅回路
トランジスタ回路:エミッタ接地増幅回路
 
OpeLa: セルフホストなOSと言語処理系を作るプロジェクト
OpeLa: セルフホストなOSと言語処理系を作るプロジェクトOpeLa: セルフホストなOSと言語処理系を作るプロジェクト
OpeLa: セルフホストなOSと言語処理系を作るプロジェクト
 
自作言語でお絵描き
自作言語でお絵描き自作言語でお絵描き
自作言語でお絵描き
 
OpeLa 進捗報告 at 第23回自作OSもくもく会
OpeLa 進捗報告 at 第23回自作OSもくもく会OpeLa 進捗報告 at 第23回自作OSもくもく会
OpeLa 進捗報告 at 第23回自作OSもくもく会
 
サイボウズ・ラボへ転籍して1年を振り返る
サイボウズ・ラボへ転籍して1年を振り返るサイボウズ・ラボへ転籍して1年を振り返る
サイボウズ・ラボへ転籍して1年を振り返る
 
USB3.0ドライバ開発の道
USB3.0ドライバ開発の道USB3.0ドライバ開発の道
USB3.0ドライバ開発の道
 
Security Nextcamp remote mob programming
Security Nextcamp remote mob programmingSecurity Nextcamp remote mob programming
Security Nextcamp remote mob programming
 
Langsmith OpeLa handmade self-hosted OS and LPS
Langsmith OpeLa handmade self-hosted OS and LPSLangsmith OpeLa handmade self-hosted OS and LPS
Langsmith OpeLa handmade self-hosted OS and LPS
 
OpeLa セルフホストなOSと言語処理系の自作
OpeLa セルフホストなOSと言語処理系の自作OpeLa セルフホストなOSと言語処理系の自作
OpeLa セルフホストなOSと言語処理系の自作
 
自動でバグを見つける!プログラム解析と動的バイナリ計装
自動でバグを見つける!プログラム解析と動的バイナリ計装自動でバグを見つける!プログラム解析と動的バイナリ計装
自動でバグを見つける!プログラム解析と動的バイナリ計装
 
1を書いても0が読める!?隠れた重要命令INVLPG
1を書いても0が読める!?隠れた重要命令INVLPG1を書いても0が読める!?隠れた重要命令INVLPG
1を書いても0が読める!?隠れた重要命令INVLPG
 
レガシーフリーOSに必要な要素技術 legacy free os
レガシーフリーOSに必要な要素技術 legacy free osレガシーフリーOSに必要な要素技術 legacy free os
レガシーフリーOSに必要な要素技術 legacy free os
 
Building libc++ for toy OS
Building libc++ for toy OSBuilding libc++ for toy OS
Building libc++ for toy OS
 
プランクトンサミットの歴史2019
プランクトンサミットの歴史2019プランクトンサミットの歴史2019
プランクトンサミットの歴史2019
 
Introduction of security camp 2019
Introduction of security camp 2019Introduction of security camp 2019
Introduction of security camp 2019
 
30分で分かる!OSの作り方 ver.2
30分で分かる!OSの作り方 ver.230分で分かる!OSの作り方 ver.2
30分で分かる!OSの作り方 ver.2
 
Timers
TimersTimers
Timers
 

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 を理解しよう