web-dev-qa-db-ja.com

C ++ 11 autoキーワードではいくらですか?

複雑なテンプレート型用のC++ 11標準で利用可能な新しいautoキーワードを使用してきました。しかし、私はそれを次のようなことにも使用しています:

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

より懐疑的に:

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

このトピックに関する議論はあまり見ていません。タイプは多くの場合、ドキュメントと健全性チェックの形式であるため、autoは使い古されているようです。 autoを使用してどこで線を引きますか。また、この新しい機能の推奨される使用例は何ですか?

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

サイドノート:この質問はSE.Programmersに移動され、Stack Overflowに戻りました。これについての議論はこれで見つけることができます メタ質問

206
Alan Turing

一目で型を書くのが難しいときはいつでもautoキーワードを使うべきだと思いますが、式の右辺の型は明らかです。たとえば、次を使用します。

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

intであることがわかっていても、boost::multi_indexの複合キータイプを取得します。将来変更される可能性があるため、単にintと書くことはできません。この場合、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)
121

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

59
Jon Purdy

簡単です。 do n't careタイプの場合に使用します。例えば

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を使用せずに、目的の型のオブジェクトを構築します。

can副作用を引き起こすことなくコードの冗長性を削除する場合、mustそうすることをお勧めします。

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

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

ここでは、型が何であるかを気にするので、Someclass i;for(unsigned x = y;...を記述する必要があります。

47
spraff

頑張れ。 autoを使用すると、コードの記述が簡単になります。

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

しかし、次のような数行以上のコードで作業している場合

auto foo = bla();

タイプが0回示されている場合、これらの行を変更してタイプを含めることができます。最初の例は、型が1回記述されているため素晴らしいです。autoを使用すると、面倒なテンプレート型を2回記述する必要がなくなります。 Hooray for C++++。しかし、近くの行で簡単に表示されない場合、タイプを明示的にゼロ回表示すると、少なくともC++とそのすぐ後継で、私は緊張します。抽象化、多態性、汎用性を高めて、より高いレベルで動作するように設計された他の言語については、問題ありません。

44
DarenW

はい、読みやすさを損なうほど使いすぎる可能性があります。正確な型が長い、発言できない、または読みやすさにとって重要ではなく、変数の寿命が短いコンテキストで使用することをお勧めします。たとえば、イテレータタイプは通常長いため重要ではないため、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();
36
Gene Bushuyev

C++ and Beyond 2012Ask Us Anything)パネルには、 a Andrei Alexandrescu、Scott Meyers、Herb Sutterの間でautoを使用する場合と使用しない場合について話し合う素晴らしい交流 。 4分間のディスカッションのために25:03分にスキップします。 3人のスピーカーはすべて、autoを使用するnotの場合に留意すべき優れた点を提供します。

私は人々が自分の結論に達することを強くお勧めしますが、私の重要なポイントは、autoをどこでも使用することでしたunless

  1. 読みやすさが痛い
  2. 自動型変換(たとえば、コンストラクター、割り当て、テンプレート中間型、整数幅間の暗黙的な変換など)に懸念がある

explicitを自由に使用すると、後者への懸念を減らすことができ、前者が問題になる時間を最小限に抑えることができます。

ハーブが言ったことを言い換えると、「X、Y、Zをしていないなら、autoを使用します。X、Y、Zが何であるかを学び、他のどこでもautoを使用します。」

35
Sean

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.
18
morotspaj

私はauto制限なしで使用していますが、問題はありませんでした。 intのような単純な型に使用することさえあります。これにより、c ++は私にとってより高いレベルの言語になり、pythonのようにc ++で変数を宣言できます。 pythonコードを書いた後、私は時々.

auto i = MyClass();

の代わりに

MyClass i;

これは、autoキーワードの乱用だと言う場合の1つです。

多くの場合、オブジェクトの正確なタイプは気にせず、その機能性にもっと興味があり、関数名は一般に、それらが返すオブジェクトについて何かを言うので、autoは傷つきません: auto s = mycollection.size()sは整数の一種であると推測できますが、まれに正確な型を気にする場合は、関数プロトタイプをチェックしましょう(つまり、 int_type s = mycollection.size())のように、いつの日か役に立つ場合に備えて、コードが書かれているときのアプリオリではなく、情報が必要なときにチェックしてください。

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

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

私のコードでは、この場合でもautoを使用します。xを無署名にする場合は、make_unsignedという名前のユーティリティ関数を使用します。

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

免責事項:my useについて説明しているだけで、アドバイスする能力はありません!

10
rafak

C++プログラムのmajor問題の1つは、uninitializedを使用できることです。変数。これは、厄介な非決定論的なプログラムの動作につながります。現在のコンパイラーは、プログラムが使い果たした場合に適切な/メッセージの警告メッセージをスローすることに注意してください。

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

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

最新のコンパイラ(GCC)を使用してこのプログラムをコンパイルすると、警告が表示されます。実際の複雑な量産コードを使用している場合、このような警告はあまりはっきりしないかもしれません。

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

main.cpp:4:8:warning: 'x'はこの関数で初期化されずに使用されます[-Wuninitialized]

y + = x;

    ^

================================================== =====================================今、auto、コンパイルすると次のようになります:

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

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

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

 auto x;

      ^

autoでは、初期化されていない変数を使用することはできません。これは、auto

この概念と他のすばらしい最新のC++の概念は、C++の専門家 Herb ShutterCppCon14 トークで説明されています。

基本に戻る!モダンC++スタイルの要点

3
Mantosh Kumar

私が指摘した危険の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();
2
user2495422

autoキーワードはローカル変数にのみ使用でき、引数またはクラス/構造体メンバーには使用できません。だから、あなたが好きな場所でそれらを使用することは安全で実行可能です。よく使います。型はコンパイル時に推測され、デバッガはデバッグ中に型を表示し、sizeofはそれを正しく報告し、decltypeは正しい型を与えます-害はありません。私はautoを使いすぎとしてカウントしません。

1
Ajay

型を推論するのに意味がある場合は、autoを使用します。整数であることがわかっているもの、または文字列であることがわかっている場合は、int/std :: stringなどを使用してください。またはコードを難読化します。

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

1
LainIwakura

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

受け入れられた答え は、次の経験則を示唆しています。

一目で型の書き方を言うのが難しいときはいつでもautoを使用しますが、式の右側の型は明らかです。

しかし、私はそれがあまりにも制限的であると言うでしょう。いつか私は型について気にしません。なぜなら、型を理解するのに時間をかけることを気にせずに文が十分に有益であるからです。それはどういう意味ですか?いくつかの答えに現れた例を考えてみましょう:

auto x = f();

これがautoの誤用の例になっているのはなぜですか? f()の戻り値のタイプを知らないのですか?まあ、それを知っていれば本当に役立つかもしれませんが、それは私の主な関心事ではありません。さらに問題なのは、xf()がほとんど意味がないということです。私たちが持っていた場合:

auto nugget = mine_gold();

代わりに、関数の戻り値の型が明らかであるかどうかは通常気にしません。ステートメントを読んで、私は自分が何をしているかを知っており、戻り値のセマンティクスがその型も知る必要があると感じないように十分に知っています。

したがって、私の答えは次のとおりです。コンパイラが許可する場合は常にautoを使用します。

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

そしてまた:

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