Upgrade to Pro — share decks privately, control downloads, hide ads and more …

C++17の新機能 optional編 / new features of C++17 - optional

C++17の新機能 optional編 / new features of C++17 - optional

C++17 で追加された optional の紹介です。

突貫で作ったので冗長な上にいろいろ間違ってるかもです。
あと使用例が無いのは単なる力不足です…

Miutsuru kariya

November 22, 2018
Tweet

More Decks by Miutsuru kariya

Other Decks in Programming

Transcript

  1. C++17 の新機能
    optional編
    2018/11/22 鳥頭かりやマン
    1

    View Slide

  2. 言い訳
    optional 全く分かってなくて突貫で
    でっち上げたので、間違いや不足
    がめっさあると思います。
    訂正・補足よろしくお願いします。
    2

    View Slide

  3. optional objects
    3

    View Slide

  4. optional objects
    4
    N1878 A proposal to add an utility class to represent optional objects (Revision 1)
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1878.htm
    N3406 A proposal to add a utility class to represent optional objects (Revision 2)
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3406.html
    N3527 A proposal to add a utility class to represent optional objects (Revision 3)
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3527.html
    N3672 A proposal to add a utility class to represent optional objects (Revision 4)
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3672.html
    N3765 On Optional
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3765.pdf
    N3793 A proposal to add a utility class to represent optional objects (Revision 5)
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html
    N3966 Fixes for optional objects
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3966.html
    N3982 Rvalue reference overloads for optional
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3982.html

    View Slide

  5. optional objects
    5
    N4078 Fixes for optional objects
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4078.html
    P0032R0 Homogeneous interface for variant, any and optional
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0032r0.pdf
    P0032R1 Homogeneous interface for variant, any and optional (Revision 1)
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0032r1.pdf
    P0032R2 Homogeneous interface for variant , any and optional (Revision 2)
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0032r2.pdf
    P0307R0 Making Optional Greater Equal Again
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0307r0.pdf
    P0032R3 Homogeneous interface for variant, any and optional (Revision 3)
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0032r3.pdf
    P0307R2 Making Optional Greater Equal Again
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0307r2.pdf
    P0504R0 Revisiting in-place tag types for any/optional/variant
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0504r0.html

    View Slide

  6. optional objects
    提案ペーパーどんだけあんねん…
    6

    View Slide

  7. optional objects
    7
    導入の背景

    View Slide

  8. optional objects
    導入の背景
    8
    あらゆる型において
    「値が無い」と言う状態を
    統一的に表したい!

    View Slide

  9. optional objects
    導入の背景
    9
    値が無い状態を使いたい時

    View Slide

  10. optional objects 導入の背景
    値が無い状態を使いたい時
    10
    1. 関数から戻す値が無い時がある
    2. 関数にオプション引数がある
    3. null 状態を表したい
    4. 寿命を手動で制御したい
    5. 高コストな初期化を避けたい
    ※ 提案ペーパーから適当訳で引用

    View Slide

  11. optional objects 導入の背景
    値が無い状態を使いたい時
    11
    1. 関数から戻す値が無い時がある
    C 言語の getchar() みたいな感じ。
    普通は返す char あるんだけど、たまに無い、
    みたいな。
    getchar() はそのために戻り値型が char じゃなくて int。
    getchar() の戻り値を char で受けて EOF との比較がうまくいかなかったり、逆に
    int で受けたものを char と比較する際に char 側は負数になってるのに int 側は
    正数になっててハマった初心者は少なく見積もっても5000万人はいるはずだ!

    View Slide

  12. optional objects 導入の背景
    値が無い状態を使いたい時
    12
    2. 関数にオプション引数がある
    引数で渡すべき値が無い、みたいな。
    デフォルト引数はあくまでもその引数型の値
    しか渡せないので。
    オーバーロードで解決可能な場合もあるけど、
    例えばオプション引数が 3 つあったらオー
    バーロード 8 個必要になる…

    View Slide

  13. optional objects 導入の背景
    値が無い状態を使いたい時
    13
    2. 関数にオプション引数がある
    提案ペーパーにあった例
    void execute( function fa, function fb )
    {
    int i = computeI();
    fa(i); // implicit assumption that fa != nullptr
    if (fb) {
    fb(i);
    }
    }
    まあ見ての通り std::function は null にできるんだけどね、もっと汎用性あるもの
    が欲しいよね…

    View Slide

  14. optional objects 導入の背景
    値が無い状態を使いたい時
    14
    3. null 状態を表したい
    変数で「値を保持していない」と言う状態を表
    したい、みたいな。
    メンバ変数で構築直後は null 状態にしてお
    きたい、とか…

    View Slide

  15. optional objects 導入の背景
    値が無い状態を使いたい時
    15
    4. 寿命を手動で制御したい
    オブジェクトの構築、破棄のタイミングをス
    コープではなく手動で制御したい、でも例外
    とか発生した場合は自動で破棄して欲しい、
    みたいな。
    だいぶワガママやな…

    View Slide

  16. optional objects 導入の背景
    値が無い状態を使いたい時
    16
    4. 寿命を手動で制御したい
    提案ペーパーにあった例
    Result run3Actions( Parameter param )
    {
    Resource1 res1; // まだ使う前
    Resource2 res2; // まだ使う前
    runAction1( res1 ); // ↑
    : // res1 はこの範囲だけ使いたい
    runAction2( res1, res2 ); // ↓ // ↑
    : // res2 はこの範囲だけ使いたい
    runAction3( res2 ); // ↓
    }
    でも例外が発生した場合には res1 と res2 は使ってる最中に限り、お片付けして欲しい…

    View Slide

  17. optional objects 導入の背景
    値が無い状態を使いたい時
    17
    5. 高コストな初期化を避けたい
    メンバ変数だと、構築時に初期化は避けられ
    ないけど、初期化が高コストな型の場合で、
    かつ、結局使うかどうかわからないような物
    の場合には、実際に必要に迫られるまで初
    期化コストを払いたくない、みたいな…

    View Slide

  18. optional objects
    18
    対応策

    View Slide

  19. optional objects
    対応策
    19
    T 型の値の他に、値が無い状態も
    表すことができるクラステンプレート
    optional を作ろう!

    View Slide

  20. optional objects
    対応策
    20
    optional の特徴
    • 値の有無を表すことができる。
    • 値がある場合には、その値を適切に管理してくれる。(コンストラクタ、
    デストラクタをちゃんと呼んでくれる)
    • optional 自身は動的メモリ等を使わない。(new とか呼ばない)
    • 破棄可能な型であれば std::in_place_t と std::nullopt_t(後述)を除い
    てなんでも格納できる。(std::optional 自身でさえ!)
    • 格納する値のアラインメントをちゃんと考慮してくれる。(あたりまえ)
    • コピー・ムーブできる。(ただし、格納する型が対応していれば)
    • コピーは値のセマンティクスで行われる。(値の共有とか起きない)
    • 値の有無を表す分、元の型よりはちょっとメモリ余分に食うかな…

    View Slide

  21. optional objects
    21
    構築

    View Slide

  22. optional objects
    構築
    22
    構築は、処理内容によって以下のような種類に分けられる。
    1. 値が無い状態で構築
    2. 格納する値から構築
    3. コピー・ムーブ構築
    4. 型の違う optional からコピー・ムーブ構築
    5. 値のコンストラクタ引数から直接構築
    また、上記のうち2と5は、コンストラクタだけでなく、ファクトリ
    関数でも構築できる。(特に5はファクトリ関数の方がオヌヌメ)

    View Slide

  23. optional objects
    構築
    23
    ちなみに、コンストラクタはゲロ吐きそうな
    ほどすげぇいっぱいある…
    (全部で9個)
    まぁ basic_string ほどじゃないけど…(こっちは15個)
    あと、ファクトリ関数も 3 個ある…

    View Slide

  24. optional objects
    構築
    24
    コンストラクタ
    constexpr optional() noexcept;
    constexpr optional(nullopt_t) noexcept;
    constexpr optional(const optional&);
    constexpr optional(optional&&) noexcept(see below);
    template
    constexpr explicit optional(in_place_t, Args&&...);
    template
    constexpr explicit optional(in_place_t, initializer_list,
    Args&&...);
    template
    EXPLICIT constexpr optional(U&&);
    template
    EXPLICIT optional(const optional&);
    template
    EXPLICIT optional(optional&&);

    View Slide

  25. optional objects
    構築
    25
    ファクトリ関数
    template
    constexpr optional> make_optional(T&&);
    template
    constexpr optional make_optional(Args&&... args);
    template
    constexpr optional make_optional(initializer_list il,
    Args&&... args);

    View Slide

  26. optional objects
    構築
    26
    値が無い状態で構築
    constexpr optional() noexcept;
    constexpr optional(nullopt_t) noexcept;
    引数無し(デフォルトコンストラクタ)、あるいは、引数に std::nullopt_t 型
    の値 std::nullopt を指定して構築すると、値が無い状態となる。
    std::optional v1; // v1は値を持たない
    std::optional v2{std::nullopt}; // v2も値を持たない
    なお、テンプレート型引数Tがデフォルト構築できなくても OK。
    なにしろ値がないんだからね、当然だね。

    View Slide

  27. optional objects
    構築
    27
    何で書き方が2通りあるの?
    1. 値が無いことを明示したい時に便利。特に optional の引数や戻り値の場合
    に std::nullopt って書けばわかりやすいよね。デフォルトコンストラクタの場合
    は {} って書かなきゃならなくて何のこっちゃってなる…
    func( {} ); // これってどうなのよ…
    2. そもそも optional っていうのは「T 型」と「値が無いという状態を表す型」
    の直和型なんだから、後者を表す「型」としての std::nullopt_t と、その「値」と
    しての std::nullopt があった方が自然だよね、とのこと…
    optional ≡ T ⨆ std::nullopt_t
    3. あと、デフォルト構築だと格納する値がデフォルト構築されてると勘違いしそ
    うな気もするから、個人的には std::nullopt 書かれてた方がいいかなぁ…
    std::optional v; // MyStruct 型の値がデフォルト構築
    // されるようにも見える?(見えない?

    View Slide

  28. optional objects
    構築
    28
    格納する値から構築
    // コンストラクタ
    template
    EXPLICIT constexpr optional(U&&);
    // ファクトリ関数(非メンバ関数)
    template
    constexpr optional> make_optional(T&&);
    引数が1つの場合に、その引数から格納するオブジェクトを直接構築す
    る。
    実は、見ての通り何か型がちょっと違うし、挙動も微妙に(?)違う。

    View Slide

  29. optional objects
    構築
    29
    格納する値から構築・コンストラクタ
    template
    EXPLICIT constexpr optional(U&&);
    使い方は至って普通。
    // 普通の値から構築
    std::optional v1 = 42;
    引数型が T&& じゃなくて U&& なので、実は型が違っても基本的には
    一時オブジェクトが作られない。
    // 文字列リテラルから string を optional 内に直接構築
    std::optional v2{"ゴーン has gone"};

    View Slide

  30. optional objects
    構築
    30
    ちなみに、ナゾの EXPLICIT は is_convertible_v が false の
    場合のみ explicit。
    // double から int への変換はコピー初期化でも大丈夫!
    std::optional v = 3.14;
    // double から S への変換は explicit
    struct S {
    explicit S(double) {}
    };
    // 直接初期化はできるけど、コピー初期化(v = 3.14)じゃダメだぞ!
    std::optional v{3.14};

    View Slide

  31. optional objects
    構築
    31
    実は普通の値から構築はもうちょっと簡単に書ける。
    // 普通の値から構築。何とテンプレート引数書かなくても値から int に推論!
    std::optional v1 = 42;
    このために、optional には以下のような推論補助が提供されている。
    template
    optional(T) -> optional;
    普通は推論補助なんてなくてもいいはずなんだけど、この場合は引数
    からテンプレート型引数が推論できないので…
    (引数型が T とは無関係な U&& だからね、仕方ないね…)

    View Slide

  32. optional objects
    構築
    32
    ちなみに、さっきの string の例は省略しちゃダメだよ。違うものになっ
    ちゃうから…
    // optional のつもりが optional に…
    std::optional v2{"ゴーン has gone"};
    だからと言って、引数側を string にしちゃうと、せっかく U&& で受けられ
    るようにしてるのに一時オブジェクト作っちゃうので MOTTAINAI…
    // 確かに optional にはなるけど…
    std::optional v2{"ゴーン has gone"s};

    View Slide

  33. optional objects
    構築
    33
    あと、値をデフォルト構築したい時にもこれ使っちゃいそうだけど、これも
    一時オブジェクト作っちゃうので、後述の直接構築使った方が良いよ。
    struct S {
    S() { /* デフォルトコンストラクタだぞ! */ }
    };
    // 確かに optional で S をデフォルト構築できるけど…
    std::optional v3{ S{} };
    // こっちの方が効率的だよ(詳しくは後述)
    std::optional v4{std::in_place};
    // これでも同じく効率的(これも詳しくは後述)
    auto v5 = std::make_optional();

    View Slide

  34. optional objects
    構築
    34
    ところで、テンプレート引数 U の謎のデフォルト引数 T は何?
    template
    EXPLICIT constexpr optional(U&&);
    引数が 1 つの初期化リストの場合にテンプレート型推論がうまくいかないのを防
    ぐため。…だと思う…(多分。自信なし)
    // こんなこともできるけど…
    std::optional> v({ 1, 2, 3 });
    でもこれ実はワナ。例えば上記の場合、引数の型は initializer_list じゃなく
    て array になっちゃっているので、一旦引数として構築してから
    optional 内にムーブされる。
    (しかも std::array にはムーブは無いのでコピーになる)
    初期化リスト渡すならこれじゃなくて後述の直接構築をオヌヌメします…

    View Slide

  35. optional objects
    構築
    35
    格納する値から構築・ファクトリ関数
    template
    constexpr optional> make_optional(T&&);
    こちらも使い方は至って普通。
    // 普通の値から構築
    auto v1 = std::make_optional(42);
    これだけ見ると、前述のコンストラクタでテンプレート引数省略した方が楽そう…
    でも、他のオーバーロード(後述)と共に使うことで、構築を統一的に書けるとい
    うメリットはあるかも…

    View Slide

  36. optional objects
    構築
    36
    あと、見ての通り生成される型には std::decay_t が付いているので、参照が削除
    されたうえで、配列はポインタに、関数は関数ポインタに、cv 修飾された型は cv
    就職が削除された型になる。
    template
    constexpr optional> make_optional(T&&);
    ちなみに、呼び出す際にテンプレート型引数を指定することもできるけど、そうす
    ると実引数の型と仮引数の型が一致しなくなるので、ほとんどの場合後述する
    別のオーバーロードが呼ばれちゃうと思う…(多分。自信なし)
    // これだと違うオーバーロードが呼ばれる…
    auto v = std::make_optional("ゴーン has gone");
    あ、それから C++17 で戻り値のコピー省略(RVO)が必須になったので、make_optional
    使ってもムダな戻り値のコピーとかは発生しないぞ!すごいぞ C++17!

    View Slide

  37. optional objects
    構築
    37
    コピー・ムーブ構築
    constexpr optional(const optional&);
    constexpr optional(optional&&) noexcept(see below);
    コピー・ムーブコンストラクタ。値の有無を含めてコピー・ムーブする。
    みんなが思った通りに普通に動く。
    std::optional v1 = 42;
    std::optional v2 = v1;
    なお、テンプレート型引数Tがコピー・ムーブできなければ使えない。
    そりゃあそうだよね…
    あと T のムーブコンストラクタが noexcept ならムーブコンストラクタが noexcept になる。

    View Slide

  38. optional objects
    構築
    38
    もちろんコピー・ムーブでもテンプレート型引数の推論は効くよ!
    // std::optional だぞ!
    std::optional v1 = 42;
    // これも std::optional だぞ!
    std::optional v2 = v1;
    いやでもこの場合は普通に auto 使うかな…
    // これも std::optional になるぞ!(当たり前)
    auto v3 = v1;

    View Slide

  39. optional objects
    構築
    39
    型の違う optional からコピー・ムーブ構築
    template
    EXPLICIT optional(const optional&);
    template
    EXPLICIT optional(optional&&);
    型の違う optional からもコピー・ムーブ構築できる。
    ただし、型 U から型 T への変換が出来なきゃダメ(当たり前)
    ちなみに、ナゾの EXPLICIT は「格納する値から構築」と一緒で、is_convertible_vT> が false の場合のみ explicit。

    View Slide

  40. optional objects
    構築
    40
    // optional になるぞ!
    std::optional v1 = 3.14;
    // optional から optional にコピー構築できるぞ!
    std::optional v2 = v1;
    // double から S への変換は explicit
    struct S {
    explicit S(double) {}
    };
    // optional から optional にコピー構築できるけど、
    // コピー初期化(v3 = v1)じゃダメだぞ!(ちょっと意味不明)
    std::optional v3{v1};

    View Slide

  41. optional objects
    構築
    41
    値のコンストラクタ引数から直接構築
    // 可変引数版コンストラクタ
    template
    constexpr explicit optional(in_place_t, Args&&...);
    // 可変引数版ファクトリ関数(非メンバ関数)
    template
    constexpr optional make_optional(Args&&... args);
    // 初期化リスト版コンストラクタ
    template
    constexpr explicit optional(in_place_t, initializer_list,
    Args&&...);
    // 初期化リスト版ファクトリ関数(非メンバ関数)
    template
    constexpr optional make_optional(initializer_list il,
    Args&&... args);

    View Slide

  42. optional objects
    構築
    42
    値のコンストラクタ引数から直接構築・可変引数版
    // コンストラクタ
    template
    constexpr explicit optional(in_place_t, Args&&...);
    // ファクトリ関数(非メンバ関数)
    template
    constexpr optional make_optional(Args&&... args);
    テンプレート型引数Tのオブジェクトを optional 内に直接構築する。
    コンストラクタの場合、引数の先頭に std::in_place_t 型の引数
    std::in_place を指定すると使える。
    どちらも一時オブジェクトが作られないので効率がいいけど、ナゾの
    std::in_place を指定する必要が無いのでファクトリ関数の方がオヌヌメ。

    View Slide

  43. optional objects
    構築
    43
    // 1024 個の 'w' からなる string を optional 内に直接構築
    // コンストラクタを使用(std::in_placeがウザい。てかそんなん覚えてられん)
    std::optional v1{std::in_place, 1024, 'w'};
    // 1024 個の 'w' からなる string を optional 内に直接構築
    // ファクトリ関数を使用
    auto v2 = std::make_optional(1024, 'w');
    個人的にはどう見ても make_optional の方がいいんだけど、みなさんは
    どう思います?

    View Slide

  44. optional objects
    構築
    44
    値のコンストラクタ引数から直接構築・初期化リスト版
    // コンストラクタ
    template
    constexpr explicit optional(in_place_t, initializer_list,
    Args&&...);
    // ファクトリ関数(非メンバ関数)
    template
    constexpr optional make_optional(initializer_list il,
    Args&&... args);
    やりたいことは可変引数版と一緒だが、T 型のコンストラクタ引数に初
    期化リストがあると可変引数版ではテンプレート型推論がうまく働かな
    いので、こちらのバージョンが必要。
    ちなみに、後ろにナゾの args があるのはご愛敬(標準コンテナの初期化リストコンストラク
    タにも追加の引数(アロケータ)があるのでしょうがない…)。

    View Slide

  45. optional objects
    構築
    45
    // 3 つの要素を持った vector を optional 内に直接構築
    // コンストラクタを使用(std::in_placeが(ry)
    std::optional> v1{std::in_place, { 1, 2, 3 }};
    // 3 つの要素を持った vector を optional 内に直接構築
    // ファクトリ関数を使用
    auto v2 = std::make_optional>({ 1, 2, 3 });
    個人的にはどう見て(ry

    View Slide

  46. optional objects
    構築
    46
    あと、格納する値から構築にも書いたけど、値をデフォルト構築したい場
    合も、この直接構築使うのがいいよ!
    struct S {
    S() { /* デフォルトコンストラクタだぞ! */ }
    };
    // 値をデフォルト構築するぞ!
    std::optional v1{std::in_place}; // コンストラクタ使用
    auto v2 = std::make_optional(); // ファクトリ関数使用(オヌヌメ
    // ちなみに、これじゃ値無しになっちゃうのでダメ!
    std::optional v3 = {};
    // これなら値のデフォルト構築にはなるけど、一時オブジェクト作っちゃう…
    std::optional v4{ S{} };

    View Slide

  47. optional objects
    47
    破棄

    View Slide

  48. optional objects
    破棄
    48
    デストラクタ
    ~optional();
    コンストラクタと違って、1 個しかない!(当たり前)
    ちゃんと動く。値を保持している時に限り、その値のデストラクタを呼び
    出す。
    なお、テンプレート型引数 T のデストラクタが自明(trivial)な場合、この
    デストラクタも自明となる。
    ※ 自明なデストラクタ:ざっくり言うと、オブジェクト破棄時に呼ばなくてもいいデストラクタ。
    ~optional() が自明な場合、値のデストラクタも呼ばれないかも。(呼ばなくていいので)

    View Slide

  49. optional objects
    49
    代入演算子

    View Slide

  50. optional objects
    代入演算子
    50
    代入演算子も、基本的にコンストラクタと似ているが、代入
    先(*this:代入される側)と代入元(引数:代入する側)の値
    の有無によって以下のような動きになる。
    なお、std::nullopt を代入する場合は、引数側に値無しってこ
    とで。
    あと、たとえムーブ代入でも代入元の「値の有無」は変わら
    ないので注意!(値がどうなるかは値の型による)
    代入元の値に対してデストラクタが呼ばれるわけじゃないからね…
    *this に値あり *this に値無し
    引数側に値あり 代入される 値が構築される
    引数側に値無し 値が破棄される 何も起きない

    View Slide

  51. optional objects
    代入演算子
    51
    optional& operator=(nullopt_t) noexcept;
    optional& operator=(const optional&);
    optional& operator=(optional&&) noexcept(see below);
    template
    optional& operator=(U&&);
    template
    optional& operator=(const optional&);
    template
    optional& operator=(optional&&);
    コンストラクタほどじゃないけど、わりと多い…
    何と basic_string と一緒(6個)
    けど、挙動は見ればわかると思う…

    View Slide

  52. optional objects
    代入演算子
    52
    std::optional v1 = 42; // int
    std::optional v2 = 114514; // int
    std::optional v3 = 3.14; // double
    v2 = v1; // 普通にコピー代入
    v3 = v1; // int から double に変換しながら代入
    v1 = std::nullopt; // 値ありから値無しに
    v2 = 1.41421356; // 普通の値を double から int に変換しつつ代入
    v3 = v1; // v3 も値無しに
    v1 = 42; // 値無しから再び値ありに
    見たまんまで、わりと分かりやすいと思う…

    View Slide

  53. optional objects
    53
    再構築

    View Slide

  54. optional objects
    再構築
    54
    直接構築コントラクタっぽいやつ。
    (でも規格書上では assignment にグルーピングされてる…)
    挙動としては直接構築コンストラクタとほぼ同様だが、以下
    の点が異なる。
    1. 構築前に値があった場合、まず破棄する。
    さすがに破棄しないで構築したらマズいので、当たり前っちゃあ当
    たり前だけど…
    2. 構築された値への左辺値参照(T&)を返す。
    まぁコンストラクタは値返せないもんね…

    View Slide

  55. optional objects
    再構築
    55
    template
    T& emplace(Args&&...);
    template
    T& emplace(initializer_list, Args&&...);
    名前(emplace)もやっぱり直接構築っぽい。
    2 個あるのは直接構築コンストラクタと同様の理由。
    コンストラクタと違って名前で区別できてるので、引数に std::in_place は要らないよ…

    View Slide

  56. optional objects
    再構築
    56
    // 値無しで構築
    std::optional v1;
    // 1024 個の 'w' からなる string を構築
    v1.emplace(1024, 'w');
    // 値ありで構築
    auto v2 = std::make_optional>({ 1, 2, 3 });
    // いったん破棄してから、再度 vector を構築
    v2.emplace({ 4, 5, 6 });
    普通の代入と違って、値があった場合でもいったん破棄されるのがポイ
    ント。
    それはそれでいいんだけど、string とかにある assign もあった方が良かったんじゃないか
    なぁ…(そういや最近 assign は見かけないなぁ…

    View Slide

  57. optional objects
    57
    交換

    View Slide

  58. optional objects
    交換
    58
    普通に交換される。
    挙動は2つのオブジェクトの値の有無に応じて以下のようになる。
    表から分かる通り、値のムーブと交換ができなければならない。
    値あり 値無し
    値あり 値同士に対して swap が
    呼び出される
    値あり側の値から値無し
    側にムーブ構築した後、
    値あり側の値が破棄され

    値無し 値あり側の値から値無し
    側にムーブ構築した後、
    値あり側の値が破棄され

    何も起きない

    View Slide

  59. optional objects
    交換
    59
    // メンバ関数
    void swap(optional&) noexcept(see below);
    // 非メンバ関数
    template
    void swap(optional&, optional&) noexcept(see below);
    非メンバ関数版の swap は、メンバ関数の swap を呼び出すだけ。
    なお、値のムーブ構築と交換が noexcept なら、これらの関数も noxcept となる。

    View Slide

  60. optional objects
    交換
    60
    auto v1 = std::make_optional>({ 1, 1, 4 });
    auto v2 = std::make_optional>({ 5, 1, 4 });
    // なんとなく非メンバ関数版で交換!(値が入れ替わる)
    std::swap(v1, v2);
    // v1 だけ値無しにしてみる…
    v1 = std::nullopt;
    //なんとなくメンバ関数版で交換!(v1 は元に戻る、v2 は値無しになる)
    v1.swap(v2);
    極めて普通…

    View Slide

  61. optional objects
    61
    値の存在確認

    View Slide

  62. optional objects
    値の存在確認
    62
    constexpr explicit operator bool() const noexcept;
    constexpr bool has_value() const noexcept;
    値があれば true、無ければ false を返す。どちらも同じ。
    前者はちゃんと explicit なので、if 文の条件式とかじゃなければ暗黙変
    換とかされたりはしない。(もちろん直接初期化で使えば変換される)

    View Slide

  63. optional objects
    値の存在確認
    63
    std::optional v = 42;
    auto b1 = v.has_value(); // true になる。
    // if 文で直接使うと、値の存在確認になる
    if (v) {
    std::cout << "値はあるぞ!¥n";
    }
    // bool b2 = v; 暗黙変換はされないので、これはできない。
    bool b3{v}; // これは明示的変換になるので OK で、true になる。
    auto b4 = static_cast(v); // もちろんこれでも OK。
    auto b5 = !v; // これも OK で、false になる。

    View Slide

  64. optional objects
    64
    値の参照(普通のやつ)

    View Slide

  65. optional objects
    値の参照(普通のやつ)
    65
    constexpr T& value() &;
    constexpr T&& value() &&;
    constexpr const T& value() const&;
    constexpr const T&& value() const&&;
    値を取得する。見ての通り戻り値型が参照なので、const じゃなければ
    変更もできるが、寿命には注意が必要(特に右辺値参照)。
    もし値が無い場合は std::bad_optional_access 例外が発生するので安心
    (?)。
    const&& バージョンなんているの?と思うかもしれないが、これが無いと戻り値型が const
    T& になっちゃうので(右辺値参照がいつのまにか左辺値参照に!)無いと困るし、
    mutable とかがあるともしかしたら結構有効に機能するかも…

    View Slide

  66. optional objects
    値の参照(普通のやつ)
    66
    std::optional v = 42;
    auto i = v.value(); // 値が取得されて i は 42 になる
    v.value() = 99; // 参照なので更新もできる
    v = std::nullopt;
    try {
    auto j = v.value(); // 値が無い時に参照すると例外が発生する
    } catch (std::bad_optional_access&) {
    std::cout << "ひでぶ!¥n";
    }

    View Slide

  67. optional objects
    値の参照(普通のやつ)
    67
    ちなみに、右辺値参照返ってくるからと言ってうかつに参照で受けると、
    痛い目を見るから、参照もほどほどにね!
    // ヤバい!dangling reference!!!
    auto&& v1 = std::make_optional>({ 1, 1, 4 }).value();
    // これもヤバい!dangling reference!!!
    const auto& v2 = std::make_optional>({ 5, 1, 4 }).value();
    optional を直接参照で受けてれば、C++ のナゾ機能によって寿命が
    延びるんだけど、value の結果だけ受けても寿命延びないからね…
    右辺値参照はあくまでもその式内で完結させて、せいぜい最後にムー
    ブ構築に使う程度で!
    // 値で受けてもムーブ構築されるから、そこまで高コストじゃないよ!
    auto v3 = std::make_optional>({ 6, 3, 4 }).value();

    View Slide

  68. optional objects
    68
    値の参照(デフォルト値付き)

    View Slide

  69. optional objects
    値の参照(デフォルト値付き)
    69
    template
    constexpr T value_or(U&&) const&;
    template
    constexpr T value_or(U&&) &&;
    値がある場合はその値を、無い場合は引数で指定した値(からT型の値
    を構築した結果)をデフォルト値として返す。見ての通り戻り値型は参照
    じゃなく単なる値なので、寿命とか気にする必要なくて安心。
    引数の型は T じゃなくてもいいけど、もちろん T 型の値を構築できる必
    要がある。
    ちなみに、value() と違って参照を返さないので、const& と && だけしか
    ないよ。(必要ない)
    ところで、何でこいつにはテンプレート引数 U にデフォルト値が無いんですかね…

    View Slide

  70. optional objects
    値の参照(デフォルト値付き)
    70
    std::optional v = 42;
    auto i = v.value_or(99); // 値があるので i は 42 になる!
    // v.value_or(0) = 99; // value と違って更新はできないぞ!
    // 戻り値型が参照じゃないからね、仕方ないね…
    v = std::nullopt;
    auto j = v.value_or(99); // 値が無いので j は 99 になるよ!
    auto k = v.value_or(3.14);// 型が違っても平気だけど、
    // 使われる時は当然変換されるぞ。
    // この場合 k は 3 になる!

    View Slide

  71. optional objects
    71
    値の参照(ポインタっぽく)

    View Slide

  72. optional objects
    値の参照(ポインタっぽく)
    72
    constexpr T& operator*() &;
    constexpr T&& operator*() &&;
    constexpr const T& operator*() const&;
    constexpr const T&& operator*() const&&;
    値を取得する。値が無いって言うと nullptr っぽいので、ポインタっぽくア
    クセス!
    ぱっと見 value() と全く同じように見えるけど、こちらは値が無い場合に
    はみんな大好き未定義動作になるので安心!(安心とは
    逆に言えば、こちらの実装は値の有無をチェックしなくてもいいので、
    value() より速い、…かもしれない…
    vector の at と operator[] みたいな感じ…

    View Slide

  73. optional objects
    値の参照(ポインタっぽく)
    73
    std::optional v = 42;
    auto i = *v; // 値が取得されて i は 42 になる
    *v = 99; // もちろん参照なので更新もできるぞ!
    v = std::nullopt;
    auto j = *v; // ダメ。ゼッタイ。(UB)
    ちなみに、参照の危険性は value と一緒!
    // ヒャッハー!!!(dangling reference)
    auto&& k = *make_optional>({ 1, 1, 4 });

    View Slide

  74. optional objects
    74
    値の参照(メンバアクセスっぽく)

    View Slide

  75. optional objects
    値の参照(メンバアクセスっぽく)
    75
    constexpr T* operator->();
    constexpr const T* operator->() const;
    operator* と同様、スマートポインタでのメンバアクセスっぽく。
    値が無い場合にみんな大好き未定義動作になるのも operator* と同じ。
    ただし、こちらはポインタが返るので、戻り値型で右辺値左辺値の区別ができな
    い。
    このため、たとえ右辺値に対して使ってもメンバアクセスの結果は左辺値になっ
    てしまうので、激しく注意が必要!

    View Slide

  76. optional objects
    値の参照(メンバアクセスっぽく)
    76
    struct S {
    int m = 42;
    };
    auto v = std::make_optional();
    auto i = v->m; // メンバの値が取得されて i は 42 になる
    v->m = 99; // ポインタ経由なので更新もできる(こわい
    v = std::nullopt;
    auto j = v->m; // ダメ。ゼッタイ。(UB)

    View Slide

  77. optional objects
    値の参照(メンバアクセスっぽく)
    77
    右辺値参照にはくれぐれもご注意を!
    struct S {
    int m = 42;
    void f() & { /* 左辺値版 */ }
    void f() && { /* 右辺値版 */ }
    };
    // コイツ、右辺値のくせに左辺値版が動くぞ…
    std::make_optional()->f();
    // 一時オブジェクトに代入できてどうすんねん…
    std::make_optional()->m = 99;

    View Slide

  78. optional objects
    78
    クリア

    View Slide

  79. optional objects
    クリア
    79
    void reset() noexcept;
    値を破棄する。もともと値が無ければ何もしない。
    とっても思った通りに動く。
    std::optional v = "値はあるぞ!"; // 値ありで構築
    v.reset(); // ここで値を破棄
    v.reset(); // もう一度 reset を呼んでも何もしない
    v = "復活するぞ!"; // もちろん、再度割り当てることもできる

    View Slide

  80. optional objects
    80
    比較演算子

    View Slide

  81. optional objects
    比較演算子
    81
    ハラワタが煮えくり返るほどある…
    いや、マジで…
    全30個
    まぁ数は多いけど、実際はそんなに複雑なワケじゃない…

    View Slide

  82. optional objects
    比較演算子
    82
    optional 同士の比較
    template
    constexpr bool [email protected]@(const optional&,
    const optional&);
    @@ は全部で6種類(==、!=、<、<=、>、>=)。
    T と U は異なる型で OK だけど、対応する演算子で比較可能じゃないと
    ダメ。(当たり前)
    比較のされ方を簡単に言うと、「値無し」は「どんな optional にも共通の
    値」で、かつ、「他のどんな普通の値よりも小さい、別の値」とみなされる、
    と言うこと。

    View Slide

  83. optional objects
    比較演算子
    83
    等値比較(== 、!=)の場合
    1. 両方とも値が無ければ等しい
    2. 片方しか値が無ければ等しくない
    3. 両方とも値があれば、値を対応する演算子で比較した結果が、最
    終的な結果になる
    大小比較(<、<=、>、>=)の場合
    1. 両方とも値が無ければ等しい
    2. 値が無い方は値がある方より常に小さい
    3. 両方とも値があれば、値を対応する演算子で比較した結果が、最
    終的な結果になる

    View Slide

  84. optional objects
    比較演算子
    84
    optional と nullopt_t との比較
    template
    constexpr bool [email protected]@(const optional&,
    nullopt_t ) noexcept;
    template
    constexpr bool [email protected]@(nullopt_t,
    const optional&) noexcept;
    全6種類(==、!=、<、<=、>、>=)×2種類(nullopt_t が左辺か右辺か)。
    結果は「nullopt_t」を「値の無い optional」だと思えばいいだけ。

    View Slide

  85. optional objects
    比較演算子
    85
    optional と普通の値との比較
    template
    constexpr bool [email protected]@(const optional&, const U&);
    template
    constexpr bool [email protected]@(const T&, const optional&);
    全6種類(==、!=、<、<=、>、>=)×2種類(普通の値が左辺か右辺か)。
    結果は「普通の値」を「値のある optional」だと思えばいいだけ。

    View Slide

  86. optional objects
    比較演算子
    86
    std::optional v = 42; // int
    std::optional w = 3.14; // double
    auto b1 = v < w; // 普通に比較できて false になる
    auto b2 = v == 42.0; // 普通に比較できて true になる
    v = std::nullopt;
    auto b3 = v <= w; // 値無しは値有より小さいので
    // true になる
    auto b4 = v == std::nullopt; // 値無し同士で true になる

    View Slide

  87. optional objects
    87
    ハッシュサポート

    View Slide

  88. optional objects
    ハッシュサポート
    88
    template
    struct hash>;
    std::optional に対する std::hash の特殊化。
    std::hash> がハッシュをサポートしている場合に限って
    この特殊化が提供される。
    これによって、std::optional が、みんな大好き「非順序連想コンテナ」のキー
    として使用できるようになるぞ!
    なお、値がある場合のハッシュ値は std::remove_const_t のハッシュ値と同
    一と決まっているけど、値が無い場合のハッシュ値は未規定だよ(未定義動作
    なわけじゃない)。
    ちなみに、ユニークキー系の場合は、「値無し」キーは1つしか存在できないよ!
    (当たり前)

    View Slide

  89. optional objects
    ハッシュサポート
    89
    // みんな大好き unordered_set
    std::unordered_set> us;
    // 普通に要素追加できるぞ!
    us.emplace("ゴーン has gone");
    us.emplace("ゴーン、お前だったのか…");
    // もちろん値無しも追加できるぞ!(std::nullopt でもいいよ)
    us.emplace();
    // ちょっと変わったコンストラクタだと、
    // 効率考えると std::in_place 使わなきゃならないの悲しい…
    us.emplace(std::in_place, 1024, 'w');
    // 空文字列追加とか… (値のデフォルト構築)
    us.emplace(std::in_place);

    View Slide

  90. optional objects
    ハッシュサポート
    90
    // みんな大好き unordered_map
    std::unordered_map, std::string> um;
    // 普通に要素追加できるぞ!
    um.emplace("ゴーン", "has gone");
    // 同じキーは当然存在できないぞ!
    um.emplace("ゴーン", "お前だったのか…");
    // 値無しキーの追加で std::nullopt 大活躍!
    um.emplace(std::nullopt, "値ねぇし!");
    // 値のデフォルト構築で、std::in_place が避けられない…
    um.emplace(std::in_place, "空文字だぞ!");
    // ちょっと変わったコンストラクタだと、もはや何が何だか…
    um.emplace(std::piecewise_construct,
    std::forward_as_tuple(std::in_place, 1024, 'w'),
    std::forward_as_tuple("テラワロス!"));

    View Slide

  91. optional objects
    91
    メンバ型

    View Slide

  92. optional objects
    メンバ型
    92
    using value_type = T;
    テンプレート型引数 T に渡した型を、ネストしたメンバ型 value_type で参
    照できる。
    ちょっとコンテナっぽい…
    std::optional v1 = 42; // 型推論で T は int になる
    decltype(v1)::value_type i = 114514; // これは int になる
    std::optional v2 = 3.14; // 型推論で T は double になる
    decltype(v2)::value_type d = 1.41421356; // これは double になる
    でも、これ使うかな…(メタメタしい人は使うかな?

    View Slide

  93. optional objects
    93
    その他補足

    View Slide

  94. optional objects
    その他補足
    94
    std::optional
    まあ、ぶっちゃけやめといた方が無難。
    std::optional v = false;
    if (!v) { // 値が無い時なのか、false の時なのか
    func(); // どっちだよ!!!1!
    }
    そもそも、ほとんどのケースで単なる enum でもよさそう…
    ホントに3値論理欲しいなら、boost::tribool 使うとか…

    View Slide

  95. optional objects
    その他補足
    95
    std::optional
    ポインタ自体にも nullptr あるんだから、optional にする必要なくない?
    int i = 42;
    std::optional v = &i;
    func(*v); // お前が欲しかったのはホントにポインタなのか?
    // 実は int が欲しかったんじゃないのか?

    View Slide

  96. optional objects
    その他補足
    96
    std::optional>
    いやもはや std::variant とか std::variant とかの方が
    良くない?
    std::optional> v = std::nullopt;
    func(v.value_or(114).value_or(514)); // 何て?

    View Slide

  97. optional objects
    その他補足
    97
    メモリ使用量
    optional は値の有無を覚えておく必要があるから、普通に考えて 1 バイ
    トは余分に容量食うけど、アラインメントの問題があるので思ったよりも
    大きくなっちゃうかも…
    std::cout << sizeof(double) << ',' << alignof(double) << ','
    << sizeof(std::optional) << ','
    << alignof(std::optional) << '¥n';
    64bit Linux 環境では GCC も Clang も "8,8,16,8" だった。
    まぁそりゃそうだよね…

    View Slide

  98. optional objects
    その他補足
    98
    こんなのがあると、ちょっとメモリをムダにしてる感が…
    struct S {
    std::optional b;
    std::optional e;
    };
    こんな感じの方がメモリ少なくて済むかな…
    struct S {
    double b;
    double e;
    bool bExists;
    bool eExists;
    };

    View Slide

  99. optional objects
    その他補足
    99
    テンプレート引数の制限
    テンプレート引数にはほとんどなんでも指定できるけど、以下のものは
    指定できない。
    • std::nullopt_t
    コンストラクタが曖昧になるし、何より意味が分からないので、できて
    もやらないと思う…
    • std::in_place_t
    これもコンストラクタが曖昧になるし、何より意味が分からないので、
    できてもやらないと思う…
    • 参照
    これはやりたい人いるのかな?(boost::optional は出来る)
    まぁどうしてもやりたい場合は reference_wrapper 使いましょう…

    View Slide

  100. optional objects
    その他補足
    100
    std::nullopt_t のデフォルト構築
    std::nullopt_t はデフォルト構築できない。
    できちゃうと、以下の代入が曖昧になるかららしい…
    std::optional v = 42;
    v = {}; // v は値を持たない
    C++難しいな…

    View Slide

  101. optional objects
    101

    View Slide