boost::optionalをパターンマッチする関数

Vicenteさんが標準向けに提案していた関数を元に、boost::optional用のパターンマッチ関数を作ってみました。

インタフェース:

// shand/match.hpp

namespace shand {

// 1引数版
template <class T, class F>
void match(boost::optional<T>& x, F f);

template <class T, class F>
void match(const boost::optional<T>& x, F f);

// 2引数版
template <class T, class F1, class F2>
void match(boost::optional<T>& x, F1 f1, F2 f2);

template <class T, class F1, class F2>
void match(const boost::optional<T>& x, F1 f1, F2 f2);

}

関数の型FF1F2は、以下のいずれかのシグニチャを持たせます:

  • R(T&) : optionalオブジェクトが有効な値を持っていれば呼ばれる
  • R() : optionalオブジェクトが無効な値なら呼ばれる

使用例:

#include <iostream>
#include <boost/optional.hpp>
#include <shand/match.hpp>

int main()
{
    boost::optional<int> a = 3;
    shand::match(a, [](int& x) { // 有効な値を持っていれば呼ばれる
        std::cout << x << std::endl;
    });
 
    boost::optional<int> b;
    shand::match(b, [] { // 無効な値なら呼ばれる
        std::cout << "none" << std::endl;
    });
 
    shand::match(a, // 有効な場合と無効な場合で関数を呼び分ける
        [](int& x) { std::cout << x << std::endl; },
        [] { std::cout << "none" << std::endl; }
    );
}

出力:

3
none
3

この関数は、「optionalオブジェクトが有効な状態かどうか判定してから中身を取り出す」という二段階の処理を同時に行い、「じつは無効な状態なのに中身を取り出そうとした」というミスを防ぐために使用します。

expectedmap()メンバ関数のようなものです。

実装技術の話

関数から引数の型を取り出して「有効な値用の関数か、無効の値用の関数か」を判定するのではなく、関数が特定のシグニチャで呼び出し可能かを調べるis_callableメタ関数を使用して、判定しています。

is_callable<F, T&>::value == true(FT&型を受け取って呼び出し可能な関数か、が真)なら有効な値用の関数と見なしています。

is_callable<F>::value == true(引数なし)で無効な値用の関数と見なしています。

参照