105

重複の可能性:
C ++0xautoキーワードでは多すぎます

私たちは(コミュニティとして)自動車が悪用されている時期やかどうかを判断するのに十分な経験を持っていますか?

私が本当に探しているのは、に関するベストプラクティスガイドです。

  • 自動を使用する場合
  • 避けるべきとき

80%のケースですぐに従うことができる簡単な経験則。

文脈として、この質問はここでの私の応答によって引き起こされます

4

6 に答える 6

170

I think when the type is very well-known amongst the co-programmers who work (or would work) in your project, then auto can be used, such as in the following code:

//good : auto increases readability here
for(auto it = v.begin(); it != v.end(); ++it) //v is some [std] container
{
      //..
}

Or, more generally,

//good : auto increases readability here
for(auto it = std::begin(v); it != std::end(v); ++it)//v could be array as well
{
      //..
}

But when the type is not very well-known and infrequently used , then I think auto seems to reduce readability, such as here:

//bad : auto decreases readability here
auto obj = ProcessData(someVariables);

While in the former case, the usage of auto seems very good and doesn't reduce readability, and therefore, can be used extensively, but in the latter case, it reduces readabilty and hence shouldn't be used.


Another place where auto can be used is when you use new1 or make_* functions , such as here:

//without auto. Not that good, looks cumbersome
SomeType<OtherType>::SomeOtherType * obj1 = new SomeType<OtherType>::SomeOtherType();
std::shared_ptr<XyzType> obj2 = std::make_shared<XyzType>(args...);
std::unique_ptr<XyzType> obj2 = std::make_unique<XyzType>(args...);

//With auto. good : auto increases readability here
auto obj1 = new SomeType<OtherType>::SomeOtherType();
auto obj2 = std::make_shared<XyzType>(args...);
auto obj3 = std::make_unique<XyzType>(args...);

Here it is very good, as it reduces the use of keyboard, without reducing the readability, as anyone can know the type of objects being created, just by looking at the code.

1. Avoid using new and raw-pointers though.


Sometime, the type is so irrelevant that the knowledge of the type is not even needed, such as in expression template; in fact, practically it is impossible to write the type (correctly), in such cases auto is a relief for programmers. I've written expression template library which can be used as:

foam::composition::expression<int> x;

auto s = x * x;       //square
auto c = x * x * x;   //cube
for(int i = 0; i < 5 ; i++ )
    std::cout << s(i) << ", " << c(i) << std::endl; 

Output:

0, 0
1, 1
4, 8
9, 27
16, 64

Now compare the above code with the following equivalent code which doesn't use auto:

foam::composition::expression<int> x;

//scroll horizontally to see the complete type!!
foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<int>, foam::composition::expression<int>, foam::operators::multiply>> s = x * x; //square
foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<int>, foam::composition::expression<int>, foam::operators::multiply> >, foam::composition::expression<int>, foam::operators::multiply>> c = x * x * x; //cube

for(int i = 0; i < 5 ; i++ )
    std::cout << s(i) << ", " << c(i) << std::endl; 

As you can see, in such cases auto makes your life exponentially easier. The expressions used above are very simple; think about the type of some more complex expressions:

auto a = x * x - 4 * x + 4; 
auto b = x * (x + 10) / ( x * x+ 12 );
auto c = (x ^ 4 + x ^ 3 + x ^ 2 + x + 100 ) / ( x ^ 2 + 10 );

The type of such expressions would be even more huge and ugly, but thanks to auto, we now can let the compiler infer the type of the expressions.


So the bottomline is: the keyword auto might increase or decrease clarity and readability of your code, depending on the context. If the context makes it clear what type it is, or at least how it should be used (in case of standard container iterator) or the knowledge of the actual type is not even needed (such as in expression templates), then auto should be used, and if the context doesn't make it clear and isn't very common (such as the second case above), then it should better be avoided.

于 2011-08-01T15:13:36.570 に答える
21

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

for (auto i : some_container) {
   ...

ここで気にするiのは、コンテナに入っているものだけです。

typedef に少し似ています。

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

hここでは、 andwが float であるか double であるかは気にしません。ただ、 height と weight を表現するのに適した型であることに注意してください。

または検討する

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

ここで私が気にかけているのは、それが適切なイテレータであり、 をサポートしoperator++()ていることだけです。この点では、ダック タイピングのようなものです。

また、ラムダの型は綴ることができないので、auto f = []...良いスタイルです。代替手段はにキャストすることstd::functionですが、それにはオーバーヘッドが伴います。

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

副作用を発生させずにコードの冗長性をいくらか取り除くことできれば、そうするの良いことです。

于 2011-08-01T15:27:58.033 に答える
17

varC#の場合と同じルールを適用します:自由に使用してください。可読性が向上します。変数の型が実際に明示的に述べられるほど重要でない限り、その場合はこれを行う必要があります(当然)。

それでも、(特に静的に型付けされた言語では) コンパイラは、私たちよりも型の追跡に優れていると私は主張しています。ほとんどの場合、正確な型はそれほど重要ではありません (そうでなければ、インターフェイスは実際には機能しません)。どの操作が許可されているかを認識することがより重要です。コンテキストがそれを教えてくれるはずです。

さらに、初期化で不要な暗黙の変換を防ぐことにより、auto実際にバグを防ぐことができます。一般に、型がなく、暗黙的な変換が存在する場合、ステートメントFoo x = y;は暗黙的な変換を実行します。これが、そもそも暗黙の変換を避ける理由です。残念ながら、C++ にはすでにそれらが多すぎます。yFoo

書くことで、原則としてauto x = y;この問題を防ぐことができます。

一方、整数内の特定のバイト数を想定して計算を実行する場合、変数の明示的な型を知っていなければならず、明確に述べる必要があることは明らかです。

すべてのケースが明確であるとは限りませんが、ほとんどのケースがそうであると私は主張します。

  1. ほとんどの場合、明示的な型を知る必要があるかどうかは簡単にわかります。
  2. 明示的な型が必要になることは比較的まれです。

C# コンパイラ チームの主任開発者であるEric Lippertは、var .

于 2011-08-01T15:28:30.160 に答える
5

あなたの最初の質問に対する答えはノーだと思います。いつ使用または避けるべきかについていくつかのガイドラインをまとめるのに十分な知識がありますautoが、まだかなりの数のケースが残っています.

ほとんど使用しなければならない明らかなケースは、(たとえば) 2 つのジェネリック パラメーターに対する何らかの操作の結果を保持する適切な型が必要な場合のテンプレートです。このような場合、悪用の唯一の可能性はそれ自体の悪用ではありませんがauto、実行している一般的な操作の種類 (または作成しているテンプレートの種類など) があなたがするものであるかどうかです。避けたほうがいい。

また、明らかに避ける必要がある状況が少なくともいくつかありますauto。目前の仕事の一部を行うためにproxy->targetからの変換に依存しているプロキシタイプのようなものを使用している場合auto、ソースと同じタイプのターゲットを作成(しようと)するので、変換起こりません。場合によっては、変換が遅延するだけかもしれませんが、まったく機能しない場合もあります (たとえば、プロキシ タイプが代入をサポートしていない場合がよくあります)。

もう 1 つの例は、外部インターフェイスのようなもののために、特定の変数が特定の型を持つことを保証する必要がある場合です。たとえば、ネットワーク マスクを IP (v4) アドレスに適用することを検討してください。議論のために、アドレスの個々のオクテット (たとえば、それぞれを として表すunsigned char) を扱っていると仮定すると、最終的には のような結果になりoctets[0] & mask[0]ます。C の型昇格規則のおかげで、両方のオペランドがunsigned chars であっても、通常、結果は になりますint。結果は、 (通常は 4 オクテット)ではなく、ただし (つまり、1 オクテット) である必要があります。そのため、この状況では、ほぼ間違いなく不適切です。unsigned charintauto

ただし、それでも判断が必要な場合がたくさんあります。これらのケースに対する私自身の傾向autoは、デフォルトとして扱い、上記で引用した後者のケースに少なくとも少し似ているケースでのみ明示的なタイプを使用することです-正しい操作に特定のタイプが必要ない場合でもたとえそれが暗黙の変換を伴うかもしれないとしても、私は本当に特定の型が欲しい.

私の推測では (これは推測にすぎませんが)、時間の経過とともに、おそらくさらにその方向に傾くでしょう。コンパイラが型を選択することに慣れるにつれて、型を指定する必要があると現在考えているかなりの数のケースが、実際には必要なく、コードがうまくいくことがわかります。

私は、私たちの多く (そして、年をとったり経験を積んだりするほど、おそらくそれについては悪くなるでしょう) が、最終的にはパフォーマンスについての何らかの感情にさかのぼる理由で明示的な型を使用し、私たちの選択がパフォーマンスを向上させると信じているのではないかと思います。 . 場合によっては、私たちが正しいことさえあるかもしれませんが、多くの経験を持つ私たちのほとんどが気付いているように、私たちの推測はしばしば間違っています (特に暗黙の仮定に基づいている場合)。時間の経過とともに。

于 2011-08-01T16:52:53.847 に答える
2

完全な型推論で言語を使用しました。技術的に可能なすべての場所に配置しない理由はありませんauto*。実際、私はすでに を書いているかもしれauto i = 0;ませintauto。一番下の理由は、私はマニフェストの型付けを気にしないからです。

*: たとえば、auto int[] = { 0, 1, 2, 3 }動作しません。

于 2011-08-01T15:52:45.803 に答える
1

長いテンプレートやラムダ関数型などの長い反復型でのみ使用してください。物事を明確にすることができれば、それを避けるようにしてください。

于 2011-08-01T15:14:19.710 に答える