ペイントソフト開発日誌

日々の出来事を殴り書き

C++erは全員戻り値型の後置記法を使おう

目次

「戻り値型の後置記法」って何?

C++11から導入された言語仕様です。 タイトルの呼び方は自分が勝手に使っているだけで、英語圏だと「trailing-return-type」がよく使われるらしい。 cpprefjp.github.io

この仕様を使うと例えば以下のようなコードが

int Add(int a, int b)
{ return a + b; }

このようになります。

auto Add(int a, int b) -> int
{ return a + b; }

何が嬉しいの?

この仕様はC++メタプログラミングで戻り値型の推論を簡単に行う目的で使用されます。
なんだ、そんなC++上級者のための機能なんて下々の人間には関係ないじゃん、とお思いかもしれませんが、この機能にはもう一つわかりやすい大きなメリットがあるのです。
それはズバリ...

関数名でインデントが揃えやすい

例えば以下のようなクラスがあったとします。

class Human
{
public:
    Human(int age, const std::string& name);

    int GetAge() const;
    std::string GetName() const;

private:
    int m_age;
    std::string m_name;
};

このクラスでは2つのメンバ関数がありますが、関数名の位置がずれているため気持ち悪さを感じるかもしれません。

class Human
{
public:
    Human(int age, const std::string& name);

    int         GetAge() const;
    std::string GetName() const;

private:
    int m_age;
    std::string m_name;
};

スペースを入れて関数名の位置を揃えました。
いや~、プログラムがきれいになるのは気持ちがいいですね。
ん?子供の名前の一覧を取得するメンバ関数が欲しい?...しょうがないなぁ

class Human
{
public:
    Human(int age, const std::string& name);

    int         GetAge() const;
    std::string GetName() const;
    std::vector<std::string> GetChildNameList() const;

private:
    int m_age;
    std::string m_name;
    std::vector<std::string> m_childNameList;
};

文章で説明したほうが早かったかもしれませんね。
つまり、関数を定義する際にそのまま並べてしまうと、関数名が揃わないので可読性が下がってしまう。
かといって、関数名で揃えてしまうと、後々名前が長い戻り値型の関数を追加するときに困ってしまう、というジレンマが起きているのです。
別案として戻り値型で改行する方法もありますが、行数がかさむのでスマートとは言えません。
しかし「戻り値型の後置記法」を使うと...

class Human
{
public:
    Human(int age, const std::string& name);

    auto GetAge() const -> int;
    auto GetName() const -> std::string;
    auto GetChildNameList() const -> std::vector<std::string>;

private:
    int m_age;
    std::string m_name;
    std::vector<std::string> m_childNameList;
};

この2つの問題をスマートに解決することができます。 いくつか問題点もありますが、見栄えがいいので自分は普段からこの書き方で実装しています。

問題点

Q.戻り値型が見づらい
A.耐えてください。

Q.IDEの自動生成が対応していない
Q.タイプ数増えるから嫌
Q.既存の実装を書き換えるのダルい
A.これはある程度どうにかできるので次回紹介します。


<追記>
解決方法も書きました。 rough-paint.hatenablog.com