C ++ 11のautoキーワードでは多すぎますか?


218

私はauto、C ++ 11標準で利用できる新しいキーワードを、それが設計されたと考えている複雑なテンプレート型に対して使用しています。しかし、私はそれを次のものにも使用しています:

auto foo = std::make_shared<Foo>();

そしてもっと懐疑的に:

auto foo = bla(); // where bla() return a shared_ptr<Foo>

私はこのトピックについてあまり議論していません。と思われるautoタイプは、多くの場合、文書化と健全性チェックの形であることから、過剰に使用することができます。使用の際にどこに線を引くautoか、この新機能の推奨される使用例は何ですか?

明確にするために、私は哲学的な意見を求めているのではありません。標準的な委員会によるこのキーワードの意図された使用をお願いします。おそらく、その意図された使用が実際にどのように実現されるかについてのコメントが必要です。

補足:この質問は、SE.Programmersに移動され、Stack Overflowに戻されました。これに関する議論は、このメタ質問にあります。


これはQ&Aサイトですが、ディスカッションサイトではありません。あなたは非常に一般的な質問をしましたが、誰もが非常に主観的なもの以外のものをあなたに与えることができるとは思えません。(だからこそ-1)
TravisG '06 / 06/22

15
@heishe、説明を追加しました。非常に一般的に質問を読んだ場合、それは主観的な意見を求めているように見えますが、実際にautoキーワードを使用した場合は、それがどのように使用されることになっているのかわかります。これは、この機能を初めて使用する人として、どのように使用するのですか?
アランチューリング

13
この議論varは、C#が導入されたとき(つまり、結局のところ、動的型付けではないという考えを人々が理解したとき)いたるところで見られました。必要に応じて、この質問から始めて、関連する質問に進むことができます。
R.マルティーニョフェルナンデス

2
@Lex:何かが合法か、そうでないか。合法的な「悪い」ものを呼ぶことは、定義により主観的です。つまり、呼び出しauto foo = bla();「悪い」ははっきり意見ではなく、この質問を行い、正確に近い票が示すものですプログラマSEにそれが関連する可能議論を、答えること、です。/
shrug

3
この問題に関するハーブサッタービュー:herbsutter.com/2013/06/13/...
rafak

回答:


131

auto型の書き方が一目でわかりにくいときはキーワードを使うべきだと思いますが、式の右辺の型は一目瞭然です。たとえば、次のように使用します。

my_multi_type::nth_index<2>::type::key_type::composite_key_type::
    key_extractor_tuple::tail_type::head_type::result_type

boost::multi_indexあることがわかっている場合でも、で複合キータイプを取得しますintint将来変更される可能性があるので、単に書くことはできません。autoこの場合、私は書きます。

したがってauto、特定のケースでキーワードが読みやすさを向上させる場合は、それを使用します。auto型が何をauto表しているかが読者に明らかな場合は、書くことができます。

ここではいくつかの例を示します。

auto foo = std::make_shared<Foo>();   // obvious
auto foo = bla();                     // unclear. don't know which type `foo` has

const size_t max_size = 100;
for ( auto x = max_size; x > 0; --x ) // unclear. could lead to the errors
                                      // since max_size is unsigned

std::vector<some_class> v;
for ( auto it = v.begin(); it != v.end(); ++it )
                                      // ok, since I know that `it` has an iterator type
                                      // (don't really care which one in this context)

18
それは非常に良い推奨のようには思えません。オブジェクトのタイプがわからないことはどれくらいの頻度ですか?(テンプレートの外、それはそうです。)そして、もしあなたがそれらがタイプするのを知らないなら、それを調べてください、怠惰ではなく、自動を使用してください。
Paul Manta

9
@Paul:多くの場合、タイプの最も重要な部分のみを知っている、または知っている必要があるだけです。たとえば、それがイテレータであることがわかりますが、それがベクトルのイテレータであるか、リンクのイテレータであるかはわかりません。リスト; それらの場合、タイプを書き留める方法を理解しようとして時間と画面スペースを費やしたくないでしょう。
ライライアン

45
C ++ダイハードが好むと好まざるとにかかわらず、C ++ 0xはC ++を使用したことがない人を引き付けます。そして、それらは至る所でautoを使用します。
Falken教授

6
@ R.MartinhoFernandes-いいえ、はっきりしているのは、あなたがそれに返しているもの bla()何であれ、ということだけですfoo
Luis Machuca 2013年

8
@LuisMachuca私の返答は、悪い変数と関数の命名の教科書の例を挙げ、型推論での読みやすさの欠如を非難することは不誠実であると言った、口正直な方法でした。
R.マルティーニョフェルナンデス

62

可能な限りautoあらゆる場所で使用します。特にconst auto、副作用の心配が少ないように使用します。明らかな場合を除いて型について心配する必要はありませんが、静的に検証されるため、繰り返しを避けることができます。auto実行不可能な場合は、を使用decltypeして、式に基づくコントラクトとして型を意味的に表現できます。あなたのコードは異なって見えますが、それは前向きな変更になります。


12
特に「const auto&」と言います
Viktor Sehr

2
より良い利用auto&&複雑な状況でedmundv.home.xs4all.nl/blog/2014/01/28/...
KindDragon

2
@KindDragon:それは良い経験則ですが、私は使用することを好むconst auto&か、const auto明示的に変異または移動したくない場合を除きます。
Jon Purdy、2014年

できる限り自動で使用してください」。これは、auto str = std::string();代わりに書く必要があるという意味std::string str;ですか?
Calmarius

4
あらゆる場所でautoを使用すると、コードを読みながら自分で型を推測する必要があるため、コードが読みにくくなり、デバッグが難しくなります。
SubMachine

52

簡単です。タイプを気にしない場合に使用します。例えば

for (const auto & i : some_container) {
   ...

ここで私が気にするiのは、コンテナに何があるかということだけです。

typedefに少し似ています。

typedef float Height;
typedef double Weight;
//....
Height h;
Weight w;

ここではhwがfloatかdouble かは関係ありません。高さと重さを表現するのに適したタイプであることだけが重要です。

または検討する

for (auto i = some_container .begin (); ...

ここで私が気にしているのは、それが適切なイテレータであり、サポートoperator++()しているということだけです。

また、ラムダのタイプはスペルできないので、auto f = []...良いスタイルです。代わりの方法はキャストstd::functionですが、オーバーヘッドが伴います。

の「乱用」は本当に想像できませんauto。私が想像できる最も近いものは、いくつかの重要な型への明示的な変換をあなた自身から奪うことです-しかし、あなたはそれを使用autoしないでしょう、あなたは望ましい型のオブジェクトを構築するでしょう。

あなたがいる場合することができ、副作用を導入することなく、あなたのコード内のいくつかの冗長性を削除し、しなければならない、そうすることは良いこと。

反例(他人の回答から借用):

auto i = SomeClass();
for (auto x = make_unsigned (y); ...)

ここではタイプを気にしますのでSomeclass i;for(unsigned x = y;...


1
ええと。それほど簡単ではありません。それはコンパイルして実行され、アイテムが重要なオブジェクトである場合は、足元で撃たれただけです。反復は、反復の各ステップでコピーコンストラクターとデストラクターを呼び出します。範囲ベースのイテレータで盲目的にautoを使用する場合は、「for(auto item:some_container)」ではなく、「for(const auto&item:some_container)」にする必要があります。
Don Hatch、

2
いつもとは限りませんが、参考にしてください。だから何?それはとは関係ありませんauto
2014年

私はあなたの最後のコメントを本当に理解していません。なぜあなたの戦略が私にはあまり良くないように見えるのか、そしてなぜ私がそれを反対票を投じているのかを説明しようとしました。
Don Hatch、

5
参照を使用するかどうかは、autoを使用するかどうかと直交しています。それを編集して参照を追加します。これは確かに通常それがしたいことですが、目前のトピックとは完全に無関係です。
2014年

43

頑張れ。autoコードを簡単に記述できる場所ならどこでも使用できます。

任意の言語のすべての新機能は、少なくとも一部のタイプのプログラマーによって過剰に使用されます。一部の経験豊富なプログラマー(初心者ではない)が適度に使いすぎた場合のみ、残りの経験豊富なプログラマーが適切な使用の境界を学習します。極端な過度の使用は通常悪いことですが、そのような過度の使用は機能の改善またはそれを置き換えるより良い機能につながる可能性があるため、良いかもしれません。

しかし、私が数行以上のコードで作業していた場合

auto foo = bla();

タイプがゼロ回示されている場合、それらの行を変更してタイプを含めることができます。最初の例は、型が一度記述されているので素晴らしいものであり、auto面倒なテンプレート型を2回記述する必要がなくなります。Hooray for C ++++。しかし、タイプをゼロ回明示的に示すと、近くの行で簡単に見られない場合、少なくともC ++とその直後のバージョンでは緊張します。抽象化、ポリモーフィズム、および汎用性を高めて、より高いレベルで機能するように設計された他の言語については、問題ありません。


38

で、C ++および2013年以降掲載会社何かのパネルあったが、使用して、使用しないようにするときの話アンドレイアレキ、スコット・マイヤーズとハーブサッター間の素晴らしい交換がauto。25分の03にスキップして4分間のディスカッションを行います。3つのスピーカーすべてが使用しない場合に留意すべき優れたポイントを提供しますauto

私は人々に自分の結論に来ることを強く勧めますが、私のテイクアウトは次の場合を除いてどこでも使用autoすることでした:

  1. 読みやすさを損なう
  2. 自動型変換に関する懸念があります(例:コンストラクター、割り当て、テンプレート中間型、整数幅間の暗黙的な変換から)

の自由な使用はexplicit、後者に対する懸念を減らすのに役立ち、前者が問題となる時間を最小限に抑えるのに役立ちます。

「X、Y、およびZを実行していない場合は、を使用してautoください。X、Y、およびZが何であるかを学び、auto他のあらゆる場所で使用してください」とHerbが言ったことを言い換えます。


4
- 「ほとんど常にオート」へのリンクもおそらく価値がherbsutter.com/2013/08/12/...
ロブ・スターリング

37

はい、読みやすくするために使いすぎることがあります。正確な型が長いか、発話できないか、読みやすさにとって重要ではなく、変数の有効期間が短いコンテキストで使用することをお勧めします。たとえば、イテレータタイプは通常長く、重要ではないのでauto機能します。

   for(auto i = container.begin(); i != container.end(); ++i);

auto ここでは読みやすさは損なわれません。

別の例は、パーサールールタイプです。これは、長く複雑な場合があります。比較:

   auto spaces = space & space & space;

r_and_t<r_and_t<r_char_t<char>&, r_char_t<char>&>, r_char_t<char>&> spaces = 
   space & space & space;

一方、型がわかっていて単純な場合は、明示的に記述した方がはるかに優れています。

int i = foo();

のではなく

auto i = foo();

2
もちろん、言語に範囲ベースのforループがあると、最初の例はそれほど刺激的ではなくなります。8v)
フレッドラーソン、

@フレッド:タイプはまだ扱いにくい場合があります(私は連想コンテナを考えています)
Matthieu M.

3
@Fred:境界がbegin()and end()でない場合、またはステップサイズが1以外の場合、またはループ時にコンテナーを変更している場合は常に、範囲ベースのforステートメントは役に立ちません。
Dennis Zickefoose

6
@geotavros:そしてr_and_t<r_and_t<r_char_t<char>&, r_char_t<char>&>, r_char_t<char>&>そうですか?
Dennis Zickefoose

7
@geotavros:またはspace、タイプを確認して検索できます。とにかく、それはより有用な情報です...結局のところ、問題は「この新しい変数のタイプ」ではなく、「どういうspace & space & space意味ですか?」式の実際のタイプは単なるノイズです。
Dennis Zickefoose

19

auto は、EigenやOpenCVなどの線形代数ライブラリでよく使用される式テンプレートと組み合わせると非常に危険な場合があります。

auto A = Matrix(...);
auto B = Matrix(...);
auto C = A * B; // C is not a matrix. It is a matrix EXPRESSION.
cout << C; // The expression is evaluated and gives the expected result.
... // <code modifying A or B>
cout << C; // The expression is evaluated AGAIN and gives a DIFFERENT result.

このタイプのミスによって引き起こされるバグは、デバッグするのが非常に困難です。可能な解決策の1つは、左から右への宣言スタイルにautoを使用している場合、結果を期待される型に明示的にキャストすることです。

auto C = Matrix(A * B); // The expression is now evaluated immediately.

1
それはそもそも奇妙な振る舞いのようです。2つの行列を乗算している場合、演算が遅延であっても、再評価できるとは思わないでしょう。最初の評価後も評価された状態を維持するはずです。元のパラメーターを変更せずにパラメーターを変更したい場合は、とにかく式を再構築する必要がありますか?それとも、元のパラメーターが常に変化しているが、手順は同じである、ストリーミング処理のような状況向けに設計されていますか?
JAB 2015年

1
A*B式がauto変数などにコピーされているかどうかに関係なく、記述した動作は引き続き存在します。
xtofl

を使用してもバグは発生しませんauto
ジムバルター2017年

@JAB:これは、操作間の単純化と相乗効果をサポートするように設計されdiag(A * B)ています。たとえば、非対角要素を計算するサイクルを無駄にする必要はありません。
Ben Voigt

また、「for(auto o:vecContainer)」のようなコードを見つけました。「o」は毎回コピーされる重いオブジェクトでした。私の推奨は、それが本来意図されていたもの、つまりテンプレート(困難な控除ガイドライン付き)と面倒なtypedefに使用することです。それでも、autoとauto&を注意深く区別する必要があります。
gast128

10

私はauto制限なしで使用し、何の問題もありませんでした。時々、のような単純なタイプにそれを使用することさえありintます。これにより、c ++は私にとってより高水準の言語になり、Pythonのようにc ++で変数を宣言できます。Pythonコードを書いた後、私は時々さえ書く

auto i = MyClass();

の代わりに

MyClass i;

これはautoキーワードの乱用と言えるケースの1つです。

多くの場合、私はそのfonctionalityでより多くの興味、および関数名は、一般的に、彼らが返すオブジェクトについて何かを言うように、オブジェクトの正確な型が何であるかを気にしないauto例には:悪くはないauto s = mycollection.size()、私はそれを推測することができますsになりますある種の整数で、正確な型が気になるまれなケースでは、関数のプロトタイプを確認してみましょう(つまり、コードを書くときにアプリオリではなく、情報が必要なときに確認する必要があります。のようにいつか役立つかもしれない場合に備えてint_type s = mycollection.size()

受け入れられた答えからのこの例に関して:

for ( auto x = max_size; x > 0; --x )

autoこの場合も引き続き使用するコードで、x署名なしにしたい場合は、sayという名前のユーティリティ関数を使用しますmake_unsigned。これは、私の懸念を明確に表しています。

for ( auto x = make_unsigned(max_size); x > 0; --x )

免責事項:私は自分の使用法について説明しいるだけです。


1
@ChristianRau:皮肉ではなかった。私はその使用を推奨しなかったことに注意してくださいauto i = MyClass()
rafak 2013年

3
フォローアップ:参照:herbsutter.com/2013/06/13/…。そこでの使用as_unsignedが推奨されていauto w = widget{};ます。
rafak 2013年

3

C ++プログラムの主な問題の1つは、初期化されていない変数を使用できることです。これは、不快な非決定論的なプログラム動作につながります。現在のコンパイラーは、プログラムが使用するのに疲れた場合、適切な/メッセージの警告メッセージをスローするようになりました。

これを説明するために、以下のc ++プログラムを検討してください。

int main() {
    int x;
    int y = 0;
    y += x;
}

このプログラムを最新のコンパイラ(GCC)を使用してコンパイルすると、警告が表示されます。実際の複雑なプロダクションコードで作業している場合、このような警告はあまり明白ではない場合があります。

main.cpp:関数 'int main()'で:

main.cpp:4:8:警告:この関数では初期化されていない 'x'が使用されています[-Wuninitialized]

y + = x;

    ^

================================================== ===============================ここで、autoを使用するプログラムを変更した場合 、コンパイルすると次のようになります。

int main() {
    auto x;
    auto y = 0;
    y += x;
}

main.cpp:関数 'int main()'で:

main.cpp:2:10:エラー: 'auto x'の宣言に初期化子がありません

 auto x;

      ^

autoでは、初期化されていない変数を使用することはできません。これは、autoの使用を開始した場合に(無料で)取得できる主要な利点です。

このコンセプトとその他のすばらしい最新のC ++コンセプトは、C ++のエキスパートであるハーブシャッターが彼のCppCon14講演で説明しています。

基本に戻ります!最新のC ++スタイルの要点


2
はい。そして、あなたは0I、0U、0リットル、0UL、は0.0f、0.0、あるいはINT()、)(符号なし)(ダブル、などとして初期化することができますタイプを指定します
ロビンソン

6
この議論はばかげています。「コンパイラエラーが発生するため、イニシャライザを忘れることはできない」と言っていますが、を使用することを覚えておく必要がありますauto
オービットのライトネスレース

1
@LightnessRacesinOrbit:ハーブサッターの話からこの概念を実現しました。ハーブの話から論理的/実用的なアドバイスを見つけたので、コミュニティと共有すると考えました。
Mantosh Kumar

2
これがautoを使う良い動機だとは思いません。初期化されていない変数を使用するためにコンパイラの警告フラグをオンにし、すべての警告に対処するだけです。これは使用すべきでないと言っautoているわけではありませんが、この問題を回避するために使用する必要はありません。
アインポクルム2017年

2

私が指摘した1つの危険は、参照に関してです。例えば

MyBigObject& ref_to_big_object= big_object;
auto another_ref = ref_to_big_object; // ?

問題は、another_refが実際には参照ではなく、MyBigObject&ではなくMyBigObjectであることです。大きなオブジェクトを気付かずにコピーしてしまいます。

メソッドから直接参照を取得している場合は、それが実際に何であるかを考えていない可能性があります。

auto another_ref = function_returning_ref_to_big_object();

「auto&」または「const auto&」が必要になります

MyBigObject& ref_to_big_object= big_object;
auto& another_ref = ref_to_big_object;
const auto& yet_another_ref = function_returning_ref_to_big_object();

1

auto型が推論されるのが理にかなっている場所で使用します。整数であることがわかっている場合、または文字列であることがわかっている場合は、int / std :: stringなどを使用してください。ばかばかしいところに到達しない限り、言語機能を「使いすぎ」ても心配しません。またはコードを難読化します。

それはとにかく私の意見です。


4
「それが理にかなっている...」はトリッキーな部分です。プログラマーはコーディングの詳細に夢中になり、他のプログラマー、特に将来のメンテナーにとって意味のあるものを失うことになります。
DarenW

それは本当ですが、あいまいな場合は簡単にわかると思います。疑問がある場合は、コメントを使用してください!
LainIwakura

1
タイプを使用するのは理にかなっていないでしょうか?
ミハイル

一部の開発者は、型は常に推論される必要があると言います!
アラファンギオン2011

コメントを使用する......またはタイプのみを使用し、コメントは不要ですか?
user997112

1

autoキーワードはローカル変数にのみ使用でき、引数やクラス/構造体のメンバーには使用できません。そのため、どこでも好きな場所で使用することが安全で実行可能です。よく使います。タイプはコンパイル時に推定され、デバッガーはデバッグ中にタイプを表示し、sizeofそれを正しく報告し、decltype正しいタイプを提供します-害はありません。auto使いすぎだとは思わない!


0

TL; DR:下部の経験則を参照してください。

受け入れ答えは、親指の次のルールを提案しています:

使用autoそれは一見のタイプを作成する方法を言うのは難しいですが、式の右辺の型が明らかであるときはいつでも。

しかし、それは制限が多すぎると思います。時々私はタイプについて気にしません。ステートメントがタイプを理解するために時間を費やすことをせずに十分に有益であるためです。それはどういう意味ですか?いくつかの答えに現れた例を考えてみましょう:

auto x = f();

これを誤用の例にするもの autoますか?f()の戻り値の型を知らないのですか?まあ、私がそれを知っていれば確かに役立つかもしれませんが、それは私の主な関心事ではありません。問題の多くはそれでxありf()、かなり意味がありません。私たちが持っていた場合:

auto nugget = mine_gold();

その代わり、私は通常、関数の戻り値の型が明白であるかどうかを気にしません。ステートメントを読んで、私は自分のしていることを理解しています。また、戻り値のセマンティクスについて、そのタイプも知る必要がないと感じないようにするために十分な知識があります。

だから私の答えは:autoコンパイラが許可するときはいつでも使用します:

  • 変数名と初期化/割り当て式では、ステートメントの実行内容に関する十分な情報が得られないと感じています。
  • 変数名を初期化/割り当て式と組み合わせて、型がどうあるべきかについて「誤解を招く」情報を提供していると感じます-つまり、autoの代わりに何が来るかを推測しなければならない場合、推測することができます-そしてそれは間違っており、この誤った仮定はコードの後半で影響を及ぼします。
  • 別のタイプ(参照など)を強制したい。

そしてまた:

  • auto具体的な型に置き換える前に、意味のある名前(もちろん型名を含まない)を付けることをお勧めします。

-3

私の苦い経験の一つautoれるラムダ式でそれを使用しました

auto i = []() { return 0; };
cout<<"i = "<<i<<endl; // output: 1 !!!

実際、ここiはの関数ポインタに解決されますint(*)()。これは単純なものですがcout、一緒に使用するとどのようなコンパイル/ランタイムエラーが発生する可能性があるか想像してみてくださいtemplate

あなたはそのような表現を避け autoて適切なreturnタイプを置くべきです(または制御されますdecltype()

上記の例の正しい使い方は、

auto i = []() { return 0; }(); // and now i contains the result of calling the lambda  

7
あなたはそれが自動車と何が関係しているのかを説明するというひどい仕事をしました。関数を作成してから印刷しました...よろしいですか?
Dennis Zickefoose

5
@iammilind:そして、それは自動車と何の関係があるのですか?
R.マルティーニョフェルナンデス

7
()最後に言いたくなることはほとんどありません。ラムダは関数として機能するために存在し、そこから関数ポインターの変換が行われます。すぐに呼び出したい場合は、なぜラムダを使用するのですか?auto i = 0;かなりうまくいきます。
R.マルティーニョフェルナンデス

4
少なくとも、ラムダなしの、つまり同じことauto x = []() { /* .... whatever goes in here ... */ }()よりも優れているシナリオを説明できますauto x = /* .... whatever goes in here ... */;か?同じ理由で無意味だと私はどちらかと言えば無意味だと思いますauto x = 42 + y - 42
R.マルティーニョフェルナンデス

6
-1これは自動車のせいではありません。ラムダのタイプはスペルできないため、autoが必要です。関数の呼び出しを忘れた場合は、独自のルックアウトです。呼び出されていない関数ポインタをCのprintfに偶然に入れることもできます。
スプラフ2009
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.