web-dev-qa-db-ja.com

C ++ 14の自動戻り型の推論はいつ使用する必要がありますか?

GCC 4.8.0がリリースされると、C++ 14の一部である自動戻り型の推論をサポートするコンパイラがあります。 -std=c++1yを使用すると、次のことができます。

auto foo() { //deduced to be int
    return 5;
}

私の質問は次のとおりです。この機能はいつ使うべきですか?いつそれが必要で、いつコードがきれいになりますか?

シナリオ1

私が考えることができる最初のシナリオは、可能な限りです。この方法で記述できるすべての関数はそうあるべきです。これの問題は、コードが常に読みやすくなるとは限らないことです。

シナリオ2

次のシナリオは、より複雑な戻り型を回避することです。非常に軽い例として:

template<typename T, typename U>
auto add(T t, U u) { //almost deduced as decltype(t + u): decltype(auto) would
    return t + u;
}

戻り値の型がパラメーターに明示的に依存することは、場合によってはより明確になると思いますが、これが実際に問題になることはないと思います。

シナリオ3

次に、冗長性を防ぐために:

auto foo() {
    std::vector<std::map<std::pair<int, double>, int>> ret;
    //fill ret in with stuff
    return ret;
}

C++ 11では、ベクトルの代わりにreturn {5, 6, 7};だけを実行できる場合がありますが、常にうまくいくとは限らず、関数ヘッダーと関数本体の両方で型を指定する必要があります。これは純粋に冗長であり、自動復帰型の推論はその冗長性から私たちを救います。

シナリオ4

最後に、非常に単純な関数の代わりに使用できます。

auto position() {
    return pos_;
}

auto area() {
    return length_ * width_;
}

ただし、正確な型を知りたい場合は、関数を見ることがあります。そこで提供されない場合は、pos_が宣言されている場所など、コード内の別のポイントに移動する必要があります。

結論

これらのシナリオがレイアウトされている場合、実際にコードをきれいにするのにこの機能が役立つ状況であると判明するシナリオはどれですか?ここで言及しなかったシナリオについてはどうですか?この機能を使用する前に、後から噛まないように注意すべきことは何ですか?この機能がなければ、この機能がテーブルにもたらす新しい機能はありますか?

複数の質問は、これに答える視点を見つける助けとなることに注意してください。

136
chris

C++ 11では、ラムダで戻り値の型の推論を使用する場合と、auto変数を使用する場合と同様の問題が発生します。

CおよびC++ 03の質問に対する伝統的な答えは、「型を明示的にするステートメント境界を越えて、式内では通常暗黙的ですが、キャストで明示的にすることができます」です。 C++ 11およびC++ 1yには型推論ツールが導入されているため、新しい場所では型を除外できます。

申し訳ありませんが、一般的なルールを作成してこの問題を事前に解決することはできません。特定のコードを見て、読みやすいように型を指定する必要があるかどうかを自分で決定する必要があります。コードで「この型はXです」と言う方が良いのか、 「このことのタイプは、コードのこの部分を理解することとは無関係です。コンパイラは知る必要があり、おそらくそれを解決することができますが、ここで言う必要はありません」

「読みやすさ」は客観的に定義されていない[*]、さらに読者によって異なるため、スタイルガイドでは完全に満足できないコードの作成者/編集者としての責任があります。スタイルガイドが規範を指定している場合でも、さまざまな人々がさまざまな規範を好み、「読みにくい」ことになじみのないものを見つける傾向があります。そのため、特定の提案されたスタイルルールの可読性は、多くの場合、他のスタイルルールのコンテキストでのみ判断できます。

すべてのシナリオ(最初のシナリオも含む)は、誰かのコーディングスタイルに使用できます。個人的には、2番目が最も説得力のあるユースケースであると思いますが、それでもドキュメントツールに依存すると予想しています。関数テンプレートの戻り値の型がautoであることを文書化すると、decltype(t+u)として文書化すると、(できれば)信頼できる公開インターフェイスが作成されるので、あまり役に立ちません。

[*]時折、客観的な測定を試みる人がいます。統計的に重要で一般的に適用可能な結果を​​だれでも思い付くほどではありませんが、「読みやすい」という著者の本能を支持して、プログラマーは作業者によって完全に無視されます。

58
Steve Jessop

決して必要ではありません。いつすべきかについて-あなたはそれについて多くの異なる答えを得るでしょう。実際に標準の一部として受け入れられ、大部分の主要なコンパイラによって同じように十分にサポートされるまで、私はまったく言いません。

それを超えて、それは宗教的な議論になります。私は個人的には、実際の戻り値の型を入れないことでコードが明確になり、メンテナンスがはるかに簡単になります(関数の署名を見て、実際にコードを読む必要があるのに対して関数が返すものを知ることができます)、そして、ある型を返すべきだと思うのですが、コンパイラは別の型を返すと考えています(私が今まで使ったすべてのスクリプト言語で起こったように)。自動車は大きな間違いであり、助けよりもはるかに多くの痛みを引き起こすと思います。他の人は、プログラミングの哲学に合っているので、常にそれを使うべきだと言うでしょう。とにかく、これはこのサイトの範囲外です。

8
Gabe Sechan

関数の単純さとは関係ありません(この質問の 現在削除された複製 として)。

戻り値の型が固定されている(autoを使用しない)、またはテンプレートパラメーターに複雑な方法で依存している(ほとんどの場合、autoを使用し、複数ある場合はdecltypeと組み合わせてリターンポイント)。

7
Ben Voigt

実際の実稼働環境を考えてみましょう。多くの関数と単体テストは、foo()の戻り値の型にすべて相互依存しています。ここで、何らかの理由で戻り値の型を変更する必要があるとします。

戻り値の型がどこでもautoであり、foo()および関連する関数の呼び出し元が戻り値を取得するときにautoを使用する場合、必要な変更は最小限です。そうでない場合、これは非常に退屈でエラーが発生しやすい時間を意味する可能性があります。

実世界の例として、どこでも生のポインターを使用するモジュールをスマートポインターに変更するように求められました。単体テストの修正は、実際のコードよりも苦痛でした。

これを処理する方法は他にもありますが、auto戻り値型を使用するのが適切なようです。

3
Jim Wood

戻り値の型autoが完璧な例を提供したいと思います。

長い関数呼び出しのために短いエイリアスを作成したいと想像してください。 autoを使用すると、元の戻り値の型を考慮する必要はなく(将来変更される可能性があります)、ユーザーは元の関数をクリックして実際の戻り値の型を取得できます。

inline auto CreateEntity() { return GetContext()->GetEntityManager()->CreateEntity(); }

PS: this の質問に依存します。

2
kaiser

シナリオ3の場合、返されるローカル変数で関数シグネチャの戻り値の型を変更します。クライアントプログラマーが関数の戻り値を嫌うことを明確にします。このような:

シナリオ冗長性を防ぐには:

std::vector<std::map<std::pair<int, double>, int>> foo() {
    decltype(foo()) ret;
    return ret;
}

はい、自動キーワードはありませんが、原則は冗長性を防ぎ、ソースにアクセスできないプログラマーに簡単な時間を与えるために同じです。

0