質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.99%

C++ N4189のUnique Resourceの実装例について

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,314

Chironian

C++総合1位

N4189で提案されているUnique Resourceが便利そうなので、簡易版を自ソフトへ取り込もうとしています。

この資料の16ページ 8.2 Unique Resourceに実装例が記載されているのですが、何か変です。

疑問①

template<typename R,typename D>
class unique_resource{
    R resource;
    D deleter;
    bool execute_on_destruction; // exposition only

(中略)

// construction
explicit
unique_resource(R && resource, D && deleter, bool shouldrun=true) noexcept
    : resource(std::move(resource))
    , deleter(std::move(deleter))
    , execute_on_destruction{shouldrun}{}

(中略)

//factories
template<typename R,typename D>
auto
make_unique_resource( R && r,D &&d) noexcept {
return unique_resource<R,std::remove_reference_t<D>>(
    std::move(r)
    ,std::forward<std::remove_reference_t<D>>(d)
    ,true);
}


となっているのですが、std::move()しているためresourceに参照を渡せないように思います。

実験
普通に名前付きインスタンス(std::vector<unsigned> foo;のfoo)をfactoryに渡したところ、msvc 2015ではstd::move()のままでも通ったのですが、MinGW 5.2.0ではエラーになりました。

error: invalid initialization of non-const reference of type 'std::vector<unsigned int>&' from an rvalue of type 'std::remove_reference<std::vector<unsigned int>&>::type {aka std::vector<unsigned int>}'


そして、std::forward<>へ変更することでビルドと実行できるようになりました。
これは実装例のミスと考えて間違いないでしょうか?
私自身のstd::forwardの理解が浅いのでいまいち確信が持てないのです。

疑問②

次に、factoryでremove_reference<D>されていますが、この必要性が分かりません。

実験
remove_reference_t<D>が2箇所にあります。このままでも動作するのですが、両方ともremove_reference_t<>をやめて、ラムダ式を与えてみたところ問題なく動作しました。
このremove_reference_t<>は不要と考えて間違いないでしょうか?

意味もなくコードが複雑になるのは好ましくないので、不要なら削除しようと思います。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

Unique Resourceは文字通り「Uniqueな/他と共有しない排他的な」資源を扱うため、参照を渡せないのは設計通りかと思います。

普通に名前付きインスタンス(std::vector<unsigned> foo;のfoo)をfactoryに渡したところ、msvc 2015ではstd::move()のままでも通ったのですが、MinGW 5.2.0ではエラーになりました。 

MinGW 5.2.0の挙動が正しいはずです。オブジェクトは変数fooで既に管理されているため、そこからUnique Resourceを作ってはいけません/禁止されるべきです。


次に、factoryでremove_reference<D>されていますが、この必要性が分かりません。 

make_unique_resource第二引数に左辺値を渡すケースで必要になると思います。

  • 1個目:このケースではテンプレートパラメータDは左辺値参照T&に推論されるため、unique_resource::deleterを非左辺値参照とするためのremove_reference_t<D>だと思います。
  • 2個目:std::forward<std::remove_reference_t<D>>(d)std::move(d)と等価なような... Give Up.

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/04/27 15:03 編集

    yohhoyさん。ご回答ありがとうございます。

    なるほど「unique_ptrをリソースへ拡張する」ような発想なのですね。
    そして、std::move()で処理するためにfactoryの1個目はremove_reference<D>する必要があるということ納得です。2個目の方は確かに良く分からないですね。1個目と合わせた程度なのかも?

    そして、私が欲しいものはスコープから出る時に自動的に後始末するものだったので、微妙に異なるものだったということのようです。
    クラス内のメンバ関数間で共有したいリソースなのでメンバ変数とし、特定のブロック内でのみメモリ確保させたかったのです。(参照型パラメータで次々と渡しても良いのですが、仕様変更時に無駄な手間がかかるので避けたかったのです。)
    名前を変えて実装することにします。

    お陰ですっきりしました。ありがとうございました。

    【追記】
    同じN4189に記載されているscope_exitが使えました。
    単に後始末関数をこれに登録すればOK。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.99%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる