Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

optional objects 3

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

optional objects 7 導入の背景

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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 にできるんだけどね、もっと汎用性あるもの が欲しいよね…

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

optional objects 18 対応策

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

optional objects 21 構築

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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&&);

Slide 25

Slide 25 text

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);

Slide 26

Slide 26 text

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。 なにしろ値がないんだからね、当然だね。

Slide 27

Slide 27 text

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 型の値がデフォルト構築 // されるようにも見える?(見えない?

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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};

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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!

Slide 37

Slide 37 text

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 になる。

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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};

Slide 41

Slide 41 text

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);

Slide 42

Slide 42 text

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 を指定する必要が無いのでファクトリ関数の方がオヌヌメ。

Slide 43

Slide 43 text

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 の方がいいんだけど、みなさんは どう思います?

Slide 44

Slide 44 text

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 があるのはご愛敬(標準コンテナの初期化リストコンストラク タにも追加の引数(アロケータ)があるのでしょうがない…)。

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

optional objects 47 破棄

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

optional objects 49 代入演算子

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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個) けど、挙動は見ればわかると思う…

Slide 52

Slide 52 text

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; // 値無しから再び値ありに 見たまんまで、わりと分かりやすいと思う…

Slide 53

Slide 53 text

optional objects 53 再構築

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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 は見かけないなぁ…

Slide 57

Slide 57 text

optional objects 57 交換

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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); 極めて普通…

Slide 61

Slide 61 text

optional objects 61 値の存在確認

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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 になる。

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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 とかがあるともしかしたら結構有効に機能するかも…

Slide 66

Slide 66 text

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"; }

Slide 67

Slide 67 text

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();

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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 になる!

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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[] みたいな感じ…

Slide 73

Slide 73 text

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 });

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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)

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

optional objects 78 クリア

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

optional objects 80 比較演算子

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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 になる

Slide 87

Slide 87 text

optional objects 87 ハッシュサポート

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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);

Slide 90

Slide 90 text

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("テラワロス!"));

Slide 91

Slide 91 text

optional objects 91 メンバ型

Slide 92

Slide 92 text

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 になる でも、これ使うかな…(メタメタしい人は使うかな?

Slide 93

Slide 93 text

optional objects 93 その他補足

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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" だった。 まぁそりゃそうだよね…

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

optional objects 101 完