きままにブログ

プログラミングを主とした私のメモ帳です。寂しいのでコメントください笑

Optional

以前作ったOptionalにちょっと機能を付け足しました :

  • operator()(Arg...)にて再配置newによって再構築可能としました。これにより不要なコピーのオーバーヘッドがなくなります。
  • コンストラクタとデストラクタが同数呼び出されるように修正しました。コンストラクタとデストラクタによるリソース管理ができるようになりました。(後述)
  • 代入演算子の戻り値が参照でない問題を修正しました。

実装

#pragma once

#include <new>
#include <type_traits>
#include <functional>

namespace mytools {
class None {};

template <typename T>
class Optional {
public:
	Optional() : data_pointer(nullptr) {}
	Optional(T const& it) : data_pointer(new(&data) T(it)) { }
	Optional(None const& it) : data_pointer(nullptr) { }
	Optional(Optional<T> const& it) : data_pointer(new(&data) T(*it) ) { }
	template <typename ...Args>
	Optional(Args ...args) : data_pointer(new(&data) T(args...)) {}

	~Optional() {
		if (data_pointer != nullptr) {
			data_pointer->~T();
		}
	}

	Optional<T>& operator=(T const& it) {
		if (data_pointer != nullptr) {
			data_pointer->~();
		}
		data_pointer = new(&data) T(it);
		return *this;
	}
	Optional<T>& operator=(Optional<T> const& it) {
		if (data_pointer != nullptr) {
			data_pointer->~();
		}
		data_pointer = new(&data) T(*it);
		return *this;
	}
	Optional<T>& operator=(None const& it) {
		if (data_pointer != nullptr) {
			data_pointer->~();
		}
		return *this;
	}
	bool operator==(Optional<T> const& it) const {
		return data_pointer == it.data_pointer;
	}
	bool operator!=(Optional<T> const& it) const {
		return data_pointer != it.data_pointer;
	}
	T& operator*() const {
		return *data_pointer;
	}
	T* operator->() const {
		return data_pointer;
	}

	template <typename ...Args>
	Optional<T>& operator()(Args ...args) {
		if (data_pointer != nullptr) {
			data_pointer->~T();
		}
		data_pointer = new(&data) T(args...);
		return *this;
	}

	operator bool() const {
		return data_pointer != nullptr;
	}
private:
	mutable T* data_pointer;
	typename std::aligned_storage<sizeof(T), __alignof(T)>::type data;
};
}

Optionalによるリソース管理例

Optionalは通常、無効値を表すのに使えますがそれだけではありません。例えば次のような処理で困ったことはないでしょうか。

{
	C c; // どうしてもここで初期化したい
}

// ここでは使えない…

もちろん可能なかぎりこのようなことはないようにすべきですが、こう書いたほうが分かりやすかったり効率が良かったりする場合もあります。こんなときOptionalなら、スタックにメモリを確保しつつあとでplacement-newすることで再構築可能です。

Optional<C> c; // == None

{
	c(); // 再構築
}

// ここでも使える!

このようにスコープを自由に調整できる利点もあるわけです。これを遅延初期化といいます。