Your SlideShare is downloading. ×
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

unique_ptrにポインタ以外のものを持たせるとき

137

Published on

歌舞伎座.tech#8「C++初心者会」で発表した資料です。

歌舞伎座.tech#8「C++初心者会」で発表した資料です。

Published in: Engineering
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
137
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. unique_ptrにポインタ以外のもの を持たせるとき okada(@okdshin)
  • 2. unique_ptrって? C++11で登場したスマートポインタ auto_ptrの完全上位互換
  • 3. スコープを抜けたら自動でdelete #include <iostream> #include <memory> // for unique_ptr class Widget { public: ~Widget() { std::cout << "deleted" << std::endl; } }; int main() { std::unique_ptr<Widget> wp{new Widget{}}; } //output: // deleted
  • 4. newだ!! 殺せ!!!! int main() { //std::unique_ptr<Widget> wp{new Widget{}}; auto wp = std::make_unique<Widget>(); } ※make_uniqueはC++14から登場
  • 5. unique_ptrは便利 リソースを自動で開放してくれる 効率もデフォルトだとポインタと同程度で悪くない STLコンテナの要素にできる(もちろんvectorも!) デリータも設定できる
  • 6. unique_ptrにデリータを設定 // Widgetのファクトリ関数 decltype(auto) make_widget() { auto deleter = [](Widget* wp) { std::cout << "deleter is called" << std::endl; delete wp;// きちんとdeleteしておく }; return std::unique_ptr<Widget, decltype(deleter)>{ new Widget{}, std::move(deleter)}; } int main() { auto wp = make_widget(); } //output: // deleter is called // deleted ※デリータを設定する場合はmake_uniqueは使えない
  • 7. unique_ptrからshared_ptrに変換 int main() { // デリータごとunique_ptrをshared_ptrに変換可能 { std::shared_ptr<Widget> shared_wp{make_widget()}; } { auto wp = make_widget(); std::shared_ptr<Widget> shared_wp{std::move(wp)}; } } デリータもきちんと引き継がれる unique_ptr → shared_ptrは簡単にできるが逆はできない ∴ファクトリ関数の返り値型はshared_ptrではなく unique_ptrにするのがgood
  • 8. じゃあ、リソースハンドルが ポインタじゃなくてもいいんじゃ ないの?
  • 9. リソースハンドルがポインタじゃなかったら? namespace others /*他人のAPI*/ { int GetHandle() {/*略*/} // リソースハンドルはint型 void ReleaseHandle(int handle) {/*略*/} } decltype(auto) make_unique_handle() { auto deleter = [](int handle) { others::ReleaseHandle(handle); // deleteの代わりに開放関数を呼ぶ }; // コンパイルエラー!! GetHandle()の返り値はポインタじゃないよ!!! return std::unique_ptr<int, decltype(deleter)>( others::GetHandle(), std::move(deleter)); } int main() { auto h = make_unique_handle(); } 単に置き換えただけではコンパイルできない
  • 10. デリータの中でpointer型を指定 namespace others {/*略*/} // 昔ながらの関数オブジェクトクラス struct deleter { using pointer = int; // デフォルトではint*になるのを、intと指定 void operator()(int handle) { others::ReleaseHandle(handle); } }; decltype(auto) make_unique_handle() { return std::unique_ptr<int, deleter>( others::GetHandle(), deleter{}); } int main() { auto h = make_unique_handle(); } ※ラムダではメンバ型が宣言できないため 関数オブジェクトクラスを使う
  • 11. やったか……?
  • 12. やってない error: invalid operands of types 'int' and 'std::nullptr_t' to binary 'operator!=' if (__ptr != nullptr) ^ intとnullptrの比較ができずコンパイルエラーに
  • 13. どうしようもない bool operator!=(int, std::nullptr_t)は宣言できない bool my::operator!=(int, std::nullptr_t)' must have an argument of class or enumerated type bool operator!=(int i, std::nullptr_t n); ^ もしハンドルが組み込み型じゃなくて、ユーザ定義型 だったらできるけど、そもそもなんでハンドルをnullptr なんかと比較しなくちゃいけないの?
  • 14. しょうがないので自分で型を書く class unique_handle { void safe_release() { if(handle_ == others::NullHandle) { return; } others::ReleaseHandle(handle_); handle_ = others::NullHandle; } int handle_; public: unique_handle() : handle_(others::GetHandle()) {} unique_handle(unique_handle&& rhs) : handle_{others::NullHandle} { std::swap(handle_, rhs.handle_); } decltype(auto) operator=(unique_handle&& rhs) { safe_release(); std::swap(handle_, rhs.handle_); return *this; }
  • 15. め、めんどくせえーッ!!! 例外安全性は? ムーブはちゃんと実装できてる? リソース漏れ本当にしない? どのハンドルに対しても実装はほとんど同じなのにハ ンドルごとに毎回書くの?
  • 16. 任意の型のリソースハンドルに スコープを抜けたら自動で指定し たデリータを実行してくれて ムーブできて 標準ライブラリ並に信頼できる RAIIラッパがほしい!!!!
  • 17. あります。
  • 18. N4189 Generic Scope Guard RAII Wrapper for the Standard Library unique_resource
  • 19. unique_reource C++標準化委員会のペーパーで次期標準ライブラリに加 えることを提案されているライブラリ unique_ptrの一般化
  • 20. unique_resourceによる実装 namespace others {/*略*/} decltype(auto) make_unique_handle() { return std::experimental::make_unique_resource( others::GetHandle(), &others::ReleaseHandle); } int main() { auto h1 = make_unique_handle(); auto h2 = make_unique_handle(); auto h3 = make_unique_handle(); h2 = std::move(h3); // move可能 h2の元々のハンドルはリリース auto h4 = make_unique_handle(); std::cout << h4.get() << std::endl; // 生のハンドルにアクセス h4.reset(); // 明示的にハンドルをリリース } 至極簡単に実装できる
  • 21. 「で? unique_resourceは いつ使えるようになるの?」 「N4189にExample実装があるから コピペしたら今すぐ使えるよ」
  • 22. N4189のunique_resourceは C++14対応コンパイラで使える C++11でも少し修正すれば使える
  • 23. まとめ unique_ptrはデリータをカスタマイズできるが、ポイ ンタ型のハンドルしか持てない 非ポインタ型ハンドルの自動リソース管理のために unique_resourceが提案されている N4189にunique_resourceの実装例があり、コピペした ら使える
  • 24. おまけ std::threadのRAIIラッパ
  • 25. Effective Modern C++ Item37 std::threadはjoinableの状態でデストラクタが呼ばれると プログラムを終了させてしまう int main() { // tが処理を終える前にデストラクタが呼ばれると実行時エラーが起こる std::thread t{[](){ std::cout << "hello" << std::endl; }}; } そのためstd::threadはどのような実行経路でもデストラ クタが呼ばれる前に非joinable状態にする必要がある すなわちデストラクタを呼ぶ前にjoinかdetachする まさにリソース管理の問題
  • 26. EMC++ Item37における解決法 自分でRAIIクラスを書く class unique_thread { // EMC++ではThreadRAII public: // デストラクト時の動作を指定できる enum class dtor_action { join, detach }; unique_thread(std::thread&& t, dtor_action a) : action_{a}, thread_{std::move(t)} {} ~unique_thread() { if(thread_.joinable()) { if(action_ == dtor_action::join) { thread_.join(); } else { thread_.detach(); } }
  • 27. unique_resourceを使った実装 デリータとしてラムダを渡すだけでOK enum class unique_thread_dtor_action { join, detach }; template<typename Func> decltype(auto) make_unique_thread(Func&& func, unique_thread_dtor_action action) { return std::experimental::make_unique_resource( std::thread{std::forward<Func>(func)}, [action](auto& thread) { if(thread.joinable()) { if(action == unique_thread_dtor_action::join) { thread.join(); } else { thread.detach(); } } });
  • 28. 参考 N4189 Generic Scope Guard and RAII Wrapper for the Standard Library (Peter Sommerlad & Andrew L.Sandoval 2014) Effective Modern C++ (Scott Meyers 2015)

×