コンテンツにスキップ

23. 無効値を表現できる型

無効値を表現できる型 Optional の基本的な使い方を学びます。

23.1 無効値を表現できる型

Optional<Type>std::optional<Type> に相当する型です。Type 型の値を持つことができ、値を持たないことを表す「無効値」を持つこともできます。

イメージとしては、サイズが 0 または 1 である Array<Type> です。有効な値を持つとき、サイズが 1 でその値にアクセスできます。無効値を持つときはサイズが 0 で、値にはアクセスできません。

初期値を与えられなかった場合、Optional<Type> 型の値は無効値を持ちます。

# include <Siv3D.hpp>

void Main()
{
	// 無効値で初期化する
	Optional<Point> pos1;

	// 有効値で初期化する
	Optional<Point> pos2 = Point{ 100, 200 };

	Print << pos1;

	Print << pos2;

	while (System::Update())
	{

	}
}

23.2 有効値を持つかを調べる

Optional 型の値 opt が有効値を持つ場合、opt.has_value()true を返します。if (opt)if(not opt) で調べることもできます。

# include <Siv3D.hpp>

void Main()
{
	Optional<Point> pos1;

	Optional<Point> pos2 = Point{ 100, 200 };

	Print << pos1.has_value();

	Print << pos2.has_value();
	
	if (not pos1)
	{
		Print << U"pos1 does not have a value";
	}

	if (pos2)
	{
		Print << U"pos2 has a value";
	}

	while (System::Update())
	{

	}
}

23.3 有効値にアクセスする

Optional 型の値 opt が有効値を持つ場合、*opt でその値にアクセスできます。opt->xopt->y のように、-> 演算子を使ってメンバにアクセスすることもできます。有効値を持たないときに値にアクセスしてはいけません。

# include <Siv3D.hpp>

void Main()
{
	Optional<Point> pos = Point{ 100, 200 };

	if (pos)
	{
		Print << *pos;

		pos->x += 20;

		pos->y += 30;

		Print << *pos;
	}

	while (System::Update())
	{

	}
}

23.4 無効値にする

noneOptional 型の無効値を表す定数です。Optional 型の値 opt に無効値を代入するには、opt = none とします。opt.reset() でも同じです。

# include <Siv3D.hpp>

void Main()
{
	Optional<Point> pos = Point{ 100, 200 };

	Print << pos.has_value();

	pos = none;

	Print << pos.has_value();

	pos = Point{ 300, 400 };

	// = none と同じ
	pos.reset();

	Print << pos.has_value();

	while (System::Update())
	{

	}
}

23.5 有効値または代わりの値を返す

Optional 型の値 opt が有効値を持つ場合、opt.value_or(defaultValue) は有効値の値を返し、無効値である場合に defaultValue を返します。

# include <Siv3D.hpp>

void Main()
{
	Optional<Point> pos1;

	Optional<Point> pos2 = Point{ 100, 200 };

    // pos1 は有効値を持たないため Point{ 0, 0 } を返す
	Print << pos1.value_or(Point{ 0, 0 });

    // pos2 は有効値を持つため Point{ 100, 200 } を返す
	Print << pos2.value_or(Point{ 0, 0 });

	while (System::Update())
	{

	}
}

23.6 if との組み合わせ

Optional 型を返す関数の戻り値を if () 内で受け取り、有効値を持つかを調べることができます。Optional を使うコードを短く書くのに役立ちます。

# include <Siv3D.hpp>

Optional<int32> GetResult1()
{
	return 123;
}

Optional<int32> GetResult2()
{
	return none;
}

void Main()
{
	if (const auto result = GetResult1())
	{
		Print << *result;
	}

	if (const auto result = GetResult2())
	{
		Print << *result;
	}

	while (System::Update())
	{

	}
}

23.7 活用例

Optional を使うことで、次のように 1 つの変数だけで「マウスの左ボタンが押された位置」と「矢印を描くか」を表現できます。

# include <Siv3D.hpp>

void Main()
{
	// マウスの左ボタンが押された位置
	Optional<Point> start;
	
	while (System::Update())
	{
		if (MouseL.down()) // マウスの左ボタンが押されたら
		{
			// マウスカーソルの位置を有効値として記録する
			start = Cursor::Pos();
		}
		else if (MouseL.up()) // マウスの左ボタンが離されたら
		{
			// 無効値にする
			start.reset();
		}

		// 有効値を持っていれば
		if (start)
		{
			// 現在のマウスカーソルの位置まで矢印を描く
			Line{ *start, Cursor::Pos() }.drawArrow(6, SizeF{ 20, 20 }, Palette::Orange);
		}
	}
}