読者です 読者をやめる 読者になる 読者になる

ぷろみん

プログラミング的な内容を扱ってます

std::experimental::optional

モチベーション

ついにoptionalが標準ライブラリに来て嬉しかったので。

optional

C++17にはoptionalが追加されるようです。 optionalを使うと以下のような処理が改善されます。

取得に失敗する関数を書く時どうしますか?

#include<iostream>

struct Foo
{
    Foo()=default;
    std::string message_;
};

// こう書きたいけど
Foo* Get(bool flag){
    // fooはローカルオブジェクトなので
    Foo foo;
    foo.message_ = "ok";
    if(flag){
        // 返したら不定値のポインタになってしまう
        return &foo;
    }
    else{
        return nullptr;
    }
}

// 仕方ないのでこうしよう
bool Get(bool flag, Foo *foo){
    foo->message_ = "ok";
    if(flag){
        // このパスを通ったらオブジェクトが生成されると分かりにくい
        return true;
    }
    else{
        // こちらも、このパスを通ったら無効だと分かりにくい
        return false;
    }
}

int main(){
    // 宣言と初期化が同時ではない
    Foo foo;

    // 取得と有効無効の役割が混ざっている
    if(Get(true, &foo)){
        std::cout << "1:" << foo.message_ << std::endl;
    }
    if(Get(false, &foo)){
        std::cout << "2:" << foo.message_ << std::endl;
    }
}

optionalを使うと以下の様に書けます。

#include<experimental/optional>
#include<iostream>

struct Foo
{
    Foo()=default;
    std::string message_;
};

// 戻り値が明確(値か無効値)
std::experimental::optional<Foo> Get(bool flag){
    Foo foo;
    foo.message_ = "ok";
    if(flag){
        // こちらが値を返すパスだと明確に分かる
        return foo;
    }
    else{
        return std::experimental::nullopt;
    }
}

int main(){
    // 宣言と同時に初期化できる(autoが使える)
    auto foo = Get(true);

    // 有効か無効かの真偽値で判断できる
    if(foo){
        std::cout << "1:" << foo->message_ << std::endl;
    }
    foo = Get(false);
    if(foo){
        std::cout << "2:" << foo->message_ << std::endl;
    }
}

まだまだstd::experimental::optionalが使える環境では作業できないかもしれませんが boost::optionalという選択肢もあります。