💁
スマートにエラーを示して「期待」に応えよう
最近見ているコードは、殆どの関数が戻り値のエラーコードと出力引数の両方を返すようになっているせいでかなり使いづらいです。以下のような単純な関数でも呼ぶ時は出力引数のせいで一行で呼べないです。
int get_name (std::string& name)
{
if (!check_something())
{
return -1;
}
name = m_name;
return 0;
}
std::string name;
const int ret = get_name (name);
if (ret < 0)
std::cout << "Error code: " << ret << std::endl;
else
std::cout << "Name: " << name << std::endl;
できれば、出力引数を使わずに、成功の場合の値とエラーの場合のエラーコードを戻り値で直接返すようにしたいです。
C++17 以降は、std::variant
を使えば、以下のようにどちらも戻り値で返せます。
std::variant<std::string, int> get_name()
{
if (!check_something())
{
return -1;
}
return m_name;
}
const auto name = get_name();
if (std::holds_alternative<int> (name))
std::cout << "Error code: " << std::get<int> (name) << std::endl;
else
std::cout << "Name: " << std::get<std::string> (name) << std::endl;
でもこれもあんまり使いやすくはないし、std::variant
が一般的な機能っていうのもあって、一目でどちらがエラーの場合の戻り値なのか、どちらが通常の戻り値なのかも微妙に分かりづらいです。
でも安心してください!C++23 からはこういうケースに特化したstd::expected
という機能が追加されます。
std::expected<std::string, int> get_name()
{
if (!check_something())
{
return std::unexpected (-1);
}
return m_name;
}
const auto name = get_name();
if (!name)
std::cout << "Error code: " << name.error() << std::endl;
else
std::cout << "Name: " << name.value() << std::endl;
関数の中からは、エラーの方を返す時はstd::unexpected
を使って、通常の戻り値はそのまま返せばいいです。
呼び出し側は、成功かエラーのチェックがstd::optional
に似ていて、凄く使いやすいです。error()
でエラーの場合の戻り値、value()
で通常の戻り値を取得できます。
これでようやく出力引数を使わなくてもいいようになりますね。後は C++23 が使えるようになるまで何年かかるのやら…
Discussion