Header <boost/optional.hpp>

目次

動機
開発
概要
解説
optional<bool>に関する注
例外安全の保証
型に対する要求
実装に関する注
依存関係と互換性
謝辞

動機

値を返すことになっているが、適切な返却値が無い場合がある関数について考えよう:

(A) double sqrt(double n );
(B) char get_async_input();
(C) point polygon::get_any_point_effectively_inside();

返すべき値が無いときの処置には、いくつかの方法がある。

典型的な方法の一つは、有効な返却値の存在を事後条件とすることである。つまり、関数が返却値を算出できない場合、未定義のふるまいとする(デバッグビルド時にはassert()を使っても良い)か、実行時にチェックして例外を送出する。 これは例えば関数(A)の場合には良い選択である。適切な返却値が無い、ということはすなわちパラメータが不正である(引数の値が定義域から外れている)わけで、実行を正しく継続するには、呼び出し側に、有効な範囲にある値のみを渡すよう要求するのが適切だからである。

しかし、関数(B)の場合、返すべき値が無いからといって、失敗にはならない。 もとより非同期的な関数なので、そのような状況をエラーと見なしたり、例外を送出するのは正しくないだろう。この関数は、returnする一方、何とかして返却値が無意味であることを呼び出し側に伝えなければならない。

似たようなことが関数(C)にも言える: 面積が0のポリゴンに対し、その中にある点を求めることは、本質的にエラーであるが、多くのアプリケーションでは、これをエラーとして扱うのは、性能上の理由から実用的でないため(ポリゴンが面積を持たないかどうか検出するのは、前もってテストするには高価すぎるだろうからである)、不定の座標値(典型的にはinfinity)を返すとか、そんな点は無いことを呼び出し側に知らせる手段を講じる。

関数が、その返却値が有効でないことを知らせる方法はいろいろある。その中で、オーバーヘッドが無いかまたは無視してよいほど小さいために、たいへん広く使われている一つの方法が、予約された特異値を使うことである。 EOF、string::npos、infinityの座標値を持つ点などは、そのような特異値の古典的な例である。

このような特異値がある場合、つまり返却値型が、全ての有効な値だけでなく特別な値をとることもできるとき、この方法はとても適切で、広く使われている。 不幸なことに、そんな余地が無い場合もある。そんなときは、より範囲の広い型、例えば'char'の代わりに'int'とか; またはstd::pair<point,bool>のような合成型がよく使われる。

std::pair<T,bool>を返す、つまり、結果が有効であるかどうかを示すboolのフラグを返却値に付加する手法には、pairの最初の要素に、関数が本質的に返すべき値が入っていようがいまいが、整合性のあるイディオムになりうる利点がある。 例えば、後の方の二つの関数は、次のように宣言することができるだろう:

std::pair<char,bool> get_async_input();
std::pair<point,bool> polygon::get_any_point_effectively_inside();

これらの関数は、無効かもしれない返却値を扱うとき、整合性のあるインターフェースを使う:

std::pair<point,bool> p = poly.get_any_point_effectively_inside();
if ( p.second )
  flood_fill(p.first);

しかし、この方法は、書くのが大変なだけでなく、エラーの可能性が潜んでいる。関数の結果(pairの最初の要素)を、有効な値が入っているかどうかチェックせずに、たやすく参照できてしまう。

よりよいイディオムが必要なのだ。

開発

設計

C++では、ある型Tのオブジェクト(変数)を宣言するとき、初期値を与えることができる。(初期化子によって。(c.f. 8.5))
宣言の中に空でない初期化子がある(初期値が与えられた)とき、そのオブジェクトは初期化済みであるという。
宣言に空の初期化子が使われ(初期値が与えられなかった)、デフォルトまたは値による初期化も行われなかった場合、そのオブジェクトは未初期化であるという。その値は参照可能だが、不定の初期値となる(c.f. 8.5.9)。
optional<T>は、プログラムが、あるオブジェクトが初期化されているかどうかをテストできるよう、また初期化されていないオブジェクトの値にアクセスした場合、未定義のふるまいをひき起こすことを明示できるよう、初期化済み/未初期化の概念を明確化しようと意図している。 つまり、変数がoptional<T>として宣言され、初期値が与えられなかった場合、その変数は明確に未初期化である。明確に未初期化であるoptionalオブジェクトは、本質的に全く値を持たず、その状態は実行時にテストできる。 未初期化のoptionalの値を参照することは、明確に未定義のふるまいを引き起こす。 未初期化のoptionalは、値を代入されることができる。その場合、その状態は初期化済みとなる。[ToDo:]またoptionalオブジェクトの中で初期化状態のformalな扱いが与えられ、optionalを未初期化にリセットすることもできる。

C++には、未初期化オブジェクトの概念がない。つまりオブジェクトは常に初期値を持つということである。たとえそれが不定の値であろうとも。
前節で議論したように、このことで、あるオブジェクトが有効に初期化されているかを調べるために、追加の情報が必要になる、という欠点が生じる。
この問題を扱う古典的な方法の一つが特異値である: EOF, npos, -1, 等々。 これは、型が採りうる値の集合に特異値を加えるのと同じことである。 現代的な言語では、これは、Tとenumか何か些細なPODからなる区別された共用体としてモデル化される。 区別された共用体はしばしばvariantと呼ばれる。 variantは現在の型を持っていて、今の話の場合は、Tまたは他の型のいずれかになる。 C++においては、このようなvariantは、普通は次のようなテンプレートクラスとして実装されるだろう: variant<T,nil_t>

以前にも、区別された共用体をオプショナルな値のモデルとして用いた例がある: HaskellMaybe組み込み型構築子である。

区別された共用体は、Tまたは他の型のどちらか一方のオブジェクトを格納できるコンテナと見なすことができ、オプショナルな型のラッパとして必要とするセマンティクスを全て備えている。

  • 深いコピーセマンティクス: variantのコピーは、格納されている値のコピーを意味する。
  • 深い比較セマンティクス: variantどうしの比較は、現在の型と値の両方の比較である。
  • もしvariantの現在の型がTなら、それは初期化済みのoptionalをモデル化している。
  • もしvariantの現在の型がTでなければ、それは未初期化のoptionalをモデル化している。
  • variantの現在の型がTかテストすることは、optionalが初期化されているかテストすることをモデル化する。
  • variantの現在の型がTでないのに、Tの値を抽出しようと試みることは、未初期化のoptionalの値にアクセスしようとする未定義のふるまいをモデル化する。
  • 区別された共用体をこの用途に使うため、問題になるのは現在の型がTかそうでないかということだけである。 他方の型を隠蔽するため、variantに層を被せよう。 固定長1のコンテナを、1個のTが格納されているか、何も入っていないかどちらかの、可変サイズのコンテナに変形させる。 こうしてvariant<T,nil_t>を、次のようなoptional向きのインターフェースを持つ、可変サイズで固定キャパシティ、スタックベースのコンテナにできる:

    // 未初期化のオブジェクトを構築する(内部的には、現在の型はnil_tである)
    optional<T>::optional();
    
    // 'v'で初期化されたオブジェクトを構築する(内部的には、現在の型はTである)
    optional<T>::optional( T const& v ) ;
    
    // 未初期化に戻す(現在の型はnil_tになる)
    void optional<T>::reset();
    
    // 元は初期化されていようがいまいが'v'を代入する(現在の型はTになる)
    void optional<T>::reset( T const& v ) ;
    
    // optionalが初期化済みなら'true'を、さもなくば'false'を返す。
    bool optional<T>::initialized() ;
    
    // optionalが初期化済みなら(現在の型がT)、その値への参照を返す。
    // さもなくば(現在の型がnil_t)、結果は未定義である。
    T const& optional<T>::ref() const ;
    T&       optional<T>::ref() ;
    
    // どちらも初期化済みなら、swap(T&,T&)を呼び;
    // 一方だけが初期化済みなら、reset(T const&)とreset()を呼ぶ。
    // どちらも未初期化の時は、何もしない。
    void swap ( optional<T>& lhs, optional<T>& rhs ) ;
    
    // どちらも初期化済みなら、値を比較する。
    // 一方だけが初期化済みなら、等しくない。
    // どちらも未初期化の時は、等しい。
    bool operator == ( optional<T> const& lhs, optional<T> const& rhs ) ;
    bool operator != ( optional<T> const& lhs, optional<T> const& rhs ) ;
    

    ポインタとオプショナル オブジェクト

    他の多くの言語と違って、C++では、ポインタ(または参照)によって、オブジェクトを間接的に参照することができる。 ポインタにはいくつかの特長がある。そのうち二つが、今の話題に関わってくる。

    その一つは、ポインタはそれ自身のポインタ値を持つということである。それによって、ポインタは、被参照オブジェクトを指している。 そして、ポインタのコピーは被参照オブジェクトのコピーを伴わない。 その結果、エイリアシングが発生する: 複数のポインタが同一のオブジェクトを参照しうるのである。 被参照オブジェクトのコピーを伴わないポインタのコピーのセマンティクスを、浅いコピーと呼び、それに対して深いコピーは(optional<>がそうするように)、ラッパのコピーであり、ラップされているオブジェクトのコピーを伴う。
    ポインタ(および参照)が従う文脈なので、shallow-copyは(従ってエイリアシングも)ポインタのセマンティクスの一部である。

    もう一つの関連するポインタの特長は、ポインタはヌル ポインタ値をとれるということである。 これは、ポインタが完全に何のオブジェクトも指していないことを表す特異値である。 あるいは、ヌル ポインタ値は存在しないオブジェクトの概念を表現している、とも言える。

    ヌル ポインタ値の意味するところは、ポインタがオプショナルなオブジェクトを扱う方法の、事実上の標準となることを可能にした。 実際には存在しない値を参照させるためには、適切な型のヌル ポインタ値を使うだけで済んだからである。 ポインタはオプショナルな(つまり存在しない可能性がある)オブジェクトを参照させるために、数十年もの間—C APIの日々から現代のC++ライブラリに至るまで—使われてきた; 特にオプショナルな関数引数として、またしばしばオプショナルなデータメンバのために。

    ポインタがヌルになることがあるので、被参照オブジェクトの値にアクセスする操作が、未定義になる可能性が発生する。 そのため、参照外し およびアクセス演算子を使う式、例えば: ( *p = 2 )( p->foo())は、暗黙のうちに[ToDo:]オプショナル性の概念を表現していることになる。 そしてこの情報は式のシンタックスに直結している。つまり、演算子 * および -> はそれ自身—なんら付加的な文脈なしに—包含する被参照オブジェクトが実際に存在しない限り、式は未定義になることをものがたる。
    さらに、被参照オブジェクトが実在するかは、ヌル ポインタとの比較、またはboolへの変換によってテストすることができ、習慣的に次のように書かれる: if ( p != 0 ) または if ( p )

    このようなオプショナル オブジェクトを参照するための事実上のイディオムを、コンセプトとして定式化することができる: OptionalPointee コンセプトである。
    このコンセプトは、[ToDo:]オプショナル性の概念を表現するための、*および->演算子、そしてboolへの変換を記述している。

    しかし、ポインタはオプショナル オブジェクトを参照するには適当だが、他の操作、例えば初期化や移動/コピーなどに関しては適していない。 問題はポインタのセマンティクスである浅いコピーにある: オブジェクトを効果的に移動/コピーしたいなら、ポインタだけでは充分ではない。 ポインタのコピーは、被参照オブジェクトのコピーを意味しない。例えば、動機のところで議論したように、ポインタだけではオプショナル オブジェクトを関数から返すことはできない。オブジェクトは関数から出て、呼び出し側のコンテキストに移動されねばならないからである。
    浅いコピー問題に対する、しばしば行われる解決法は、動的オブジェクトとスマートポインタを使って、この辺を自動的に処理させる方法である。 例えば、関数がオブジェクトXを[ToDo:]optionallyに返そうとする場合、返却値としてshared_ptr<X>を使うことができる。しかし、このやり方はXの動的なアロケーションを必要とする。もしXが組み込み型か小さなPODの場合、リソースの浪費だろう。 オプショナル オブジェクトは本質的に値なので、自動的な記憶域と深いコピーのセマンティクスがあると、オプショナルな値を操作するときに、普通の値を扱うのと同様にできて便利である。 ポインタにはこのセマンティクスが無い。そのため、オプショナルな値の初期化や転送のためには使われていない。 OptionalPointeeコンセプトの現世における化身たるポインタが、未定義かもしれない値へのアクセスを扱うために、いかに便利であろうとも。
    そこで、このライブラリが目指す最終的な解決は、これまでに議論してきたoptional—すなわち値ベースのコンテナ—を、OptionalPointeeコンセプトのモデルとして作り上げることである。

    OptionalPointeeコンセプトのモデルとしてのoptional<T>

    このライブラリが提供するoptional<>テンプレート クラスは、さきほど(variantに被せる層として)紹介した草案のバリエーションである。それは深いコピー深い関係演算子を持つばかりでなくOptionalPointeeコンセプトのモデルでもある。 メンバ関数'initialized()'に代えてboolへの安全な変換がある。また'ref()'メンバ関数の代わりにoperator*()とoperator->()を持つ。
    しかしとりわけ重要なのは、optional<>オブジェクトはポインタと間違えてはいけない、ということである。それらはポインタではない。optional<>はポインタのモデルではない。 例えば、optional<>には浅いコピーが無いのでエイリアシングを起こさない: 二つの異なるoptionalは決して同一のオブジェクトを参照しない。(しかし等価な値を持つことはありうる。)
    optional<T>とポインタの違いは心にとめておかねばならない。特に、関係演算子のセマンティクスが異なる: optionalの関係演算子は値を比較する; それに対してポインタの関係演算子は浅い比較を行う: 被参照オブジェクトの値の比較を行わない。
    結果として、一部の場面ではT*をoptional<T>の代わりにできるが、常にではない。特に、双方のためにジェネリックなコードを書くとき、関係演算子を直接使うことができない。代わりにテンプレート関数equal_pointees()を使わなければならない。


    概要

    namespace boost {
    
    template<class T>
    class optional
    {
      public :
    
        optional () ;
    
        explicit optional ( T const& v ) ;
    
        optional ( optional const& rhs ) ;
    
        template<class U> explicit optional ( optional<U> const& rhs ) ;
    
        optional& operator = ( optional const& rhs ) ;
    
        template<class U> optional& operator = ( optional<U> const& rhs ) ;
    
        T const* get() const ;
        T*       get() ;
    
        T const* operator ->() const ;
        T*       operator ->() ;
    
        T const& operator *() const ;
        T&       operator *() ;
    
        void reset();
    
        void reset ( T const& ) ;
    
        operator unspecified-bool-type() const ;
    
        bool operator!() const ;
    
    } ;
    
    template<class T> inline bool operator == ( optional<T> const& x, optional<T> const& y ) ;
    
    template<class T> inline bool operator != ( optional<T> const& x, optional<T> const& y ) ;
    
    template<class T> inline T* get_pointer ( optional<T> const& opt ) ;
    
    template<class T> inline void swap( optional<T>& x, optional<T>& y ) ;
    
    } // namespace boost
    

    解説

    注: 以降の節には、事後条件をサンプルコードとして示すために、多くのassert()が書かれている。Tがそれらの式をサポートする必要はないが、もしサポートしていたら、式が意味する条件は成立する。


    optional<T>::optional();

    作用: optionalをデフォルト構築する。

    事後条件: *this未初期化である。

    例外: 送出しない。

    注: Tのデフォルト コンストラクタは呼ばれない

    例:

    optional<T> def ;
    assert ( !def ) ;

    explicit optional<T>::optional( T const& v )

    作用: optionalを直接構築する。

    事後条件: *this初期化済みであり、その値は'v'のコピーとなる。

    例外: T::T( T const& )が送出する全ての例外を送出する。

    注: T::T( T const& )が呼ばれる。

    例外安全: 例外はT::T( T const& )の実行中にしか送出されない。 その場合、このコンストラクタは作用を持たない。

    例:

    T v;
    optional<T> opt(v);
    assert ( *opt == v ) ;
    

    optional<T>::optional( optional const& rhs );

    作用: optionalをコピー構築する。

    事後条件: rhsが初期化済みなら、*thisは初期化済みで、その値はrhsの値のコピーとなる; さもなくば*thisは未初期化となる。

    例外: T::T( T const& )が送出する全ての例外を送出する。

    注: rhsが初期化済みなら、T::T( T const& )が呼ばれる。

    例外安全: 例外はT::T( T const& )の実行中にしか送出されない。 その場合、このコンストラクタは作用を持たない。

    例:

    optional<T> uninit ;
    assert (!uninit);
    
    optional<T> uinit2 ( uninit ) ;
    assert ( uninit2 == uninit );
    
    optional<T> init( T(2) );
    assert ( *init == T(2) ) ;
    
    optional<T> init2 ( init ) ;
    assert ( init2 == init ) ;
    

    explicit template<U> optional<T>::optional( optional<U> const& rhs );

    作用: optionalをコピー構築する。

    事後条件: rhsが初期化済みなら、*thisは初期化済みで、その値はrhsの値を型Tに変換したもののコピーとなる; さもなくば*thisは未初期化となる。

    例外: T::T( U const& )が送出する全ての例外を送出する。

    注: rhsが初期化済みなら、T::T( U const& )が呼ばれる。UはTに正当に変換可能でなければならない。

    例外安全: 例外はT::T( U const& )の実行中にしか送出されない。 その場合、このコンストラクタは作用を持たない。

    例:

    optional<double> x(123.4);
    assert ( *x == 123.4 ) ;
    
    optional<int> y(x) ;
    assert( *y == 123 ) ;
    

    optional& optional<T>::operator= ( optional const& rhs ) ;

    作用: あるoptionalに他のoptionalを代入する。

    事後条件: rhsが初期化済みなら、*thisは初期化済みで、その値はrhsの値のコピーとなる; さもなくば*thisは未初期化となる。

    例外: T::T( T const& )が送出する全ての例外を送出する。

    注: *thisが初期化済みであったなら、最初にT::~T()によって未初期化の状態にリセットされる。その後、rhsが初期化済みであった場合、T::T( T const& )が呼ばれる。

    例外安全: 基本的: 例外はT::T( T const& )の実行中にしか送出されない。 この場合、*this未初期化のままとなる。

    例:

    T v;
    optional<T> opt(v);
    optional<T> uninit ;
    
    opt = uninit ;
    assert ( !opt ) ;
    // previous value (copy of 'v') destroyed from within 'opt'.
    
    

    template<U> optional& optional<T>::operator= ( optional<U> const& rhs ) ;

    作用: あるoptionalに、変換可能な他のoptionalを代入する。

    事後条件: rhsが初期化済みなら、*thisは初期化済みで、その値はrhsの値の型Tに変換したもののコピーとなる; さもなくば*thisは未初期化となる。

    例外: T::T( U const& )が送出する全ての例外を送出する。

    注: *thisが初期化済みであったなら、最初にT::~T()によって未初期化の状態にリセットされる。その後、rhsが初期化済みであった場合、T::T( U const& )が呼ばれる。UはTに正当に変換可能でなければならない。

    例外安全: 基本的: 例外はT::T( U const& )の実行中にしか送出されない。 この場合、*this未初期化のままとなる。

    例:

    T v;
    optional<T> opt0(v);
    optional<U> opt1;
    
    opt1 = opt0 ;
    assert ( *opt1 == static_cast<U>(v) ) ;
    

    void optional<T>::reset( T const& v ) ;

    作用: 現在の値をリセットする。

    事後条件: *thisは初期化済みで、その値は'v'のコピーとなる。

    例外: T::T( T const& )が送出する全ての例外を送出する。

    注: *thisが初期化済みであったなら、最初にT::~T()によって未初期化の状態にリセットされる。その後、T::T( T const& )が呼ばれる。

    例外安全: 基本的: 例外はT::T( T const& )の実行中にしか送出されない。 この場合、*this未初期化のままとなる。

    例:

    optional<T> opt ( some_T ) ;
    assert( *opt == some_T );
    opt.reset ( some_other_T ) ;
    assert( *opt == some_other_T );
    

    void optional<T>::reset() ;

    作用: 現在の値を破壊する。

    事後条件: *thisは未初期化となる。

    例外: 送出しない。

    注: T::~T()が呼ばれる。

    例:

    optional<T> opt ( some_T ) ;
    assert( *opt == some_T );
    opt.reset();
    assert( !opt );
    

    T const* optional<T>::get() const ;
    T*       optional<T>::get() ;
    
    inline T const* get_pointer ( optional<T> const& ) ;
    inline T*       get_pointer ( optional<T>&) ;
    

    返却値: *thisが初期化済みなら、格納されている値へのポインタ; さもなくば0 (ヌル)。

    例外: 送出しない。

    注: 格納されている値は、*thisの内部に永続的に保持されているので、このポインタを取っておいたりdeleteしたりしてはいけない。

    例:

    T v;
    optional<T> opt(v);
    optional<T> const copt(v);
    T* p = opt.get() ;
    T const* cp = copt.get();
    assert ( p == get_pointer(opt) );
    assert ( cp == get_pointer(copt) ) ;
    

    T const* optional<T>::operator ->() const ;
    T*       optional<T>::operator ->()       ;
    

    必須事項: *thisは初期化済みでなければならない。

    返却値: 格納されている値へのポインタ。

    例外: 送出しない。

    注: 必須事項はBOOST_ASSERT()でアサートされている。

    例:

    struct X { int mdata ; } ;
    X x ;
    optional<X> opt (x);
    opt->mdata = 2 ;
    

    T const& optional<T>::operator*() const ;
    T&       optional<T>::operator*();

    必須事項: *thisは初期化済みでなければならない。

    返却値: 格納されている値への参照。

    例外: 送出しない。

    注: 必須事項はBOOST_ASSERT()でアサートされている。

    例:

    T v ;
    optional<T> opt ( v );
    T const& u = *opt;
    assert ( u == v ) ;
    T w ;
    *opt = w ;
    assert ( *opt == w ) ;
    

    optional<T>::operator [ToDo:] unspecified-bool-type() const ;

    返却値: bool値の文脈で使用された場合、(get() != 0)と等価となる謎の値

    例外: 送出しない。

    例:

    optional<T> def ;
    assert ( def == 0 );
    optional<T> opt ( v ) ;
    assert ( opt );
    assert ( opt != 0 );
    

     bool optional<T>::operator!() ;

    返却値: *thisが未初期化ならtrue; さもなくばfalse

    例外: 送出しない。

    注: この演算子は、一定のbool値の文脈でunspecified-bool-typeを使うことができないコンパイラのために提供されている。

    例:

    optional<T> opt ;
    assert ( !opt );
    *opt = some_T ;
    
    // "二重否定"イディオム
    assert ( !!opt ) ;
    

    bool operator == ( optional<T> const& x, optional<T> const& y );

    返却値: xyが両方とも初期化済みなら、(*x == *y)。 xとyの一方だけが初期化済みなら、false。両方とも未初期化なら、true

    例外: 送出しない。

    注: ポインタの関係演算子は浅い比較を行うが、optionalの関係演算子は深い比較を行う。 optional<T>とポインタの両方に適用されうるジェネリックなコードに、==演算子を直接使ってはいけない; 代わりにequal_pointees()を使用せよ。

    例:

    T x(12);
    T y(12);
    T z(21);
    optional<T> def0 ;
    optional<T> def1 ;
    optional<T> optX(x);
    optional<T> optY(y);
    optional<T> optZ(z);
    
    // 同一性は常に保たれる
    assert ( def0 == def0 );
    assert ( optX == optX );
    
    // 未初期化どうしは等しい
    assert ( def0 == def1 );
    
    // 一方だけが初期化済みだと等しくない
    assert ( def0 != optX );
    
    // 両方とも初期化済みだと(*lhs == *rhs)と同じ
    assert ( optX == optY ) ;
    assert ( optX != optZ ) ;
    

    bool operator != ( optional<T> const& x, optional<T> const& y );
    

    返却値: !( x == y );

    例外: 送出しない。


    void swap ( optional<T>& x, optional<T>& y );

    作用: xyが両方とも初期化済みなら、std::swapを使ってswap(*x,*y)を呼ぶ。
    一方だけが初期化済みなら、例えばxが初期化されていれば: y.reset(*x); x.reset();が呼ばれる。
    両方とも未初期化なら何もしない。

    事後条件: xとyの内容は交換される。

    例外: 両方とも初期化済みなら、swap(T&,T&)が送出する全ての例外を送出する。 一方だけが初期化済みなら、T::T ( T const& )が送出する全ての例外を送出する。

    注: 両方とも初期化済みなら、swap(T&,T&)が修飾なしで使われるが、std::swap がスコープの中に導入されている。
    一方だけが初期化済みなら、T::~T()とT::T( T const& )が呼ばれる。

    例外安全: 両方とも初期化済みなら、swap(T&,T&)と同じ例外安全を保証する。
    一方だけが初期化済みなら、optional<T>::reset( T const& )と同じ基本的な例外安全を保証する。

    例:

    T x(12);
    T y(21);
    optional<T> def0 ;
    optional<T> def1 ;
    optional<T> optX(x);
    optional<T> optY(y);
    
    boost::swap(def0,def1); // 何もしない
    
    boost::swap(def0,optX);
    assert ( *def0 == x );
    assert ( !optX );
    
    boost::swap(def0,optX); // 元の値に戻る
    
    boost::swap(optX,optY);
    assert ( *optX == y );
    assert ( *optY == x );
    
    

    optional返却値

    optional<char> get_async_input()
    {
      if ( !queue.empty() )
           return optional<char>(queue.top());
      else return optional<char>(); // uninitialized
    }
    
    void recieve_async_message()
    {
      optional<char> rcv ;
      // The safe boolean conversion from 'rcv' is used here.
      while ( (rcv = get_async_input()) && !timeout() )
        output(*rcv);
    }
    

    optionalローカル変数

    optional<string> name ;
    if ( database.open() )
    {
      name.reset ( database.lookup(employer_name) ) ;
    }
    else
    {
      if ( can_ask_user )
        name.reset ( user.ask(employer_name) ) ;
    }
    
    if ( name )
         print(*name);
    else print("employer's name not found!");
    

    optionalデータメンバ

    class figure
    {
      public:
    
        figure()
        {
          // data member 'm_clipping_rect' is uninitialized at this point.
        }
    
        void clip_in_rect ( rect const& rect )
          {
             ....
             m_clipping_rect.reset ( rect ) ; // initialized here.
          }
    
        void draw ( canvas& cvs )
          {
            if ( m_clipping_rect )
              do_clipping(*m_clipping_rect);
    
            cvs.drawXXX(..);
          }
    
        // this can return NULL.
        rect const* get_clipping_rect() { return get_pointer(m_clipping_rect); }
    
      private :
    
        optional<rect> m_clipping_rect ;
    
    };
    

    高価で不要なデフォルト構築を避ける

    class ExpensiveCtor { ... } ;
    class Fred
    {
      Fred() : mLargeVector(10000) {}
    
      std::vector< optional<ExpensiveCtor> > mLargeVector ;
    } ;
    

    optional<bool>に関する注

    optional<bool>は特別な警戒と配慮のもとで使われなければならない。

    まず、それは機能的に3状態bool値(false, maybe, true)に似ている。—例えばboost::tribool (まだ正式にはboostの一部ではない)のような—3状態bool値では、maybe有効な値を表現していることを除いて。対してoptional<bool>では対応する状態は未初期化である。
    triboolでなくオプショナルなboolが本当に必要とされているのか、注意深く考察するべきである。

    次に、optional<>は暗黙的なboolへの変換を提供している。この変換は、初期化状態を参照するためのものであり、値とは関係ない。
    optional<bool>を使うと、暗黙的なboolへの変換のために、見つけにくいエラーを引き起こすかもしれない:

    void foo ( bool v ) ;
    void bar()
    {
      optional<bool> v = try();
    
      // foo()に'v'のを渡そうと意図している:
      foo(v);
      // しかし、打ち間違いのために、初期化状態が渡されてしまう:
      // foo(*v)と書かねばならなかった。
    }
    

    唯一の暗黙の変換はboolへのものである。しかも例の汎整数拡張が適用されない、という点で安全である(つまりfoo()が'int'の引数をとるならば、このコードはコンパイルされない)。


    例外安全の保証

    代入とreset:

    現在の実装(実装に関する注を参照)が原因で、optional<T>::operator=( optional<T> const& )とoptional<T>::reset( T const& )基本的な例外安全しか保証できない: 左辺値のoptionalは、例外が送出された場合、未初期化のままとなる(以前の値はT::~T()を使って最初に破壊される)。

    optional<T>::reset()は例外を投げない保証を提供する(T::~T()が例外を投げない限り)。

    しかし、optional<>自身はどんな例外も送出しないので、唯一の例外の源はTのコピーコンストラクタであり、T::T ( T const& )が保証する例外安全を、optionalの代入とresetも同じく保証する。

    //
    // 状況 1: 代入の過程で例外が送出された。
    //
    T v0(123);
    optional<T> opt0(v0);
    try
    {
      T v1(456);
      optional<T> opt1(v1);
      opt0 = opt1 ;
    
      // 例外が送出されていなければ、代入は成功している。
      assert( *opt0 == v1 ) ;
    }
    catch(...)
    {
      // 例外が送出されたのなら、'opt0'は未初期化にリセットされる。
      assert( !opt0 ) ;
    }
    
    //
    // 状況 2: reset(v)の過程で例外が送出された。
    //
    T v0(123);
    optional<T> opt(v0);
    try
    {
      T v1(456);
      opt.reset ( v1 ) ;
    
      // 例外が送出されていなければ、resetは成功している。
      assert( *opt == v1 ) ;
    }
    catch(...)
    {
      // 例外が送出されたのなら、'opt'は未初期化にリセットされる。
      assert( !opt ) ;
    }
    

    swap:

    void swap( optional<T>&, optional<T>& )は、optionalが両方とも初期化済みならば、swap(T&,T&)と同じ例外安全を保証する。
    もしどちらか一方だけが初期化されていたら、optional<T>::reset( T const& )と同じく基本的な例外安全を保証する(optional<T>::reset()は例外を送出しないので)。
    両方とも初期化されていないなら、何もしないので、例外を投げないことを保証する。


    型に対する要求

    TはCopy Constructibleでなければならない。Tのデストラクタは例外を投げてはならない。
    TはDefault Constructibleなくともよい


    実装に関する注

    optional<T>は、現在のところ、alignment_ofおよびtype_with_alignment(ともにType Traitsより)により構築された、アライメントつきのカスタム記憶域を用いて実装されている。個々のインスタンスが初期化されているかどうかを識別するため、bool値のフラグを使っている。
    optional値の初期化、コピー、破壊のために、Tのコピーコンストラクタを伴う配置newとTのデストラクタが明示的に使われている。
    結果として、Tのデフォルト コンストラクタは効果的に回避されるが、例外安全は基本的な保証にとどまる。
    将来的に、例外安全の保証がより強い他の実装に置き換えることが計画されている。例えば来るべきboost::variant<T,nil_t>に。


    依存関係と互換性

    実装にtype_traits/alignment_of.hppおよびtype_traits/type_with_alignment.hppを使っている。

    bcc5.5.1, vc6.0およびgcc2.95.2でテストされた。


    謝辞

    Pre-formal review:

    Peter Dimov suggested the name 'optional', and was the first to point out the need for aligned storage
    Douglas Gregor developed 'type_with_alignment', and later Eric Friedman coded 'aligned_storage', which are the core of the optional class implementation.
    Andrei Alexandrescu and Brian Parker also worked with aligned storage techniques and their work influenced the current implementation.
    Gennadiy Rozental made extensive and important comments which shaped the design.
    Vesa Karvonen and Douglas Gregor made quite useful comparisons between optional, variant and any; and made other relevant comments. Douglas Gregor and Peter Dimov commented on comparisons and evaluation in boolean contexts.
    Eric Friedman helped understand the issues involved with aligned storage, move/copy operations and exception safety.
    Many others have participated with useful comments: Aleksey Gurotov, Kevlin Henney, David Abrahams, and others I can't recall.

    Post-formal review:

    William Kempf carefully considered the originally proposed interface and suggested the new interface which is currently used. He also started and fueled the discussion about the analogy optional<>/smart pointer and about relational operators.
    Peter Dimov, Joel de Guzman, David Abrahams, Tanton Gibbs and Ian Hanson focused on the relational semantics of optional (originally undefined); concluding with the fact that the pointer-like interface doesn't make it a pointer so it shall have deep relational operators.
    Augustus Saunders also explored the different relational semantics between optional<> and a pointer and developed the OptionalPointee concept as an aid against potential conflicts on generic code.
    Joel de Guzman noticed that optional<> can be seen as an API on top of variant<T,nil_t>.
    Dave Gomboc explained the meaning and usage of the Haskell analog to optional<>: the Maybe type constructor (analogy originally pointed out by David Sankel).
    Other comments were posted by Vincent Finn, Anthony Williams, Ed Brey, Rob Stewart, and others.


    Revised January 20, 2003

    © Copyright boost.org 2003. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.

    Developed by Fernando Cacciola, the latest version of this file can be found at www.boost.org, and the boost discussion list at www.yahoogroups.com/list/boost.

    Japanese Translation Copyright (C) 2003 Yoshinori Tagawa. オリジナルの、及びこの著作権表示が全ての複製の中に現れる限り、この文書の 複製、利用、変更、販売そして配布を認める。このドキュメントは「あるがまま」 に提供されており、いかなる明示的、暗黙的保証も行わない。また、 いかなる目的に対しても、その利用が適していることを関知しない。