新的关键字“ auto”; 什么时候应该使用它来声明变量类型?

可能的复制品:
C + + 0x auto 关键字 多少才算过分

我们(作为一个社区)是否有足够的经验来判断汽车何时和/或是否被滥用?

我真正在寻找的是一个最佳实践指南

  • 何时使用自动档
  • 应该避免的时候

简单的经验法则,可以在80% 的情况下迅速遵循。

作为背景,这个问题是由我的回答 给你引发的

95047 次浏览

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.

Only use it with long repetitive types such as long templates and lambda function types. Try to avoid it if you can to make things clear.

Easy. Use it when you don't care what the type is. For example

for (auto i : some_container) {
...

All I care about here is that i is whatever's in the container.

It's a bit like typedefs.

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

Here, I don't care whether h and w are floats or doubles, only that they are whatever type is suitable to express heights and weights.

Or consider

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

Here all I care about is that it's a suitable iterator, supporting operator++(), it's kind of like duck typing in this respect.

Also the type of lambdas can't be spelled, so auto f = []... is good style. The alternative is casting to std::function but that comes with overhead.

I can't really conceive of an "abuse" of auto. The closest I can imagine is depriving yourself of an explicit conversion to some significant type -- but you wouldn't use auto for that, you'd construct an object of the desired type.

If you can remove some redundancy in your code without introducing side effects, then it must be good to do so.

I’d apply the same rule as for var in C#: use it liberally. It increases readability. Unless the type of a variable is actually important enough to be stated explicitly, in which cases this should be done (duh).

Still, I maintain that (especially in statically typed languages) the compiler is much better at tracking types for us than we are. Most of the time, the exact type isn’t terribly important anyway (otherwise interfaces wouldn’t work in practice). It’s more important to be aware of which operations are permitted. Context should tell us that.

Furthermore, auto can actually prevent bugs, by preventing unwanted implicit conversions in initialisations. Generally, the statement Foo x = y; will perform an implicit conversion if y isn’t of type Foo and an implicit conversion exists. This is the reason to avoid having implicit conversions in the first place. Unfortunately, C++ has much too many of them already.

Writing auto x = y; will prevent this problem in principle.

On the other hand, it should be clear that when I’m performing calculations that assume this or that number of bytes in an integer, the explicit type of the variable must be known and should be clearly stated.

Not all cases are as clear cut but I maintain that most are, and that

  1. in most cases it’s easy to see whether an explicit type needs to be known, and
  2. the need for explicit types is comparatively rare.

Eric Lippert, principal developer on the C# compiler team, has stated much the same with regards to var.

I've used languages with full type inference. I see no reason not to put auto everywhere it's technically possible*. In fact I may have already written auto i = 0;, where int is one character shorter than auto. I'm not even sure that I did because the bottom is: I don't care for manifest typing.

*: for instance auto int[] = { 0, 1, 2, 3 } doesn't work.

I think the answer to your first question is sort of no. We know enough to put together some guidelines about when to use or avoid auto, but they still leave quite a few cases where the best we can currently say is that we can't yet give much in the way of objective advice about them.

The obvious case where you nearly have to use it is in a template when you want (for example) the proper type to hold the result of some operation on two generic parameters. In a case like this, the only possibility of abuse wouldn't really be abuse of auto itself, but whether the general type of operation you're doing (or type of template you're writing, etc.) is something you'd be better off avoiding.

There are also at least a few situations where you clearly need to avoid auto. If you're using something like a proxy type where you're depending on the conversion from proxy->target to do part of the job at hand, auto will (attempt to) create a target of the same type as the source so that conversion won't happen. In some cases, that may just delay the conversion, but in others it won't work at all (e.g., if the proxy type doesn't support assignment, which is often the case).

Another example would be when you need to assure that a particular variable has a specific type for the sake of something like an external interface. Just for example, consider applying the network mask to an IP (v4) address. For the sake of argument, let's assume you're working with the individual octets of the address (e.g., representing each as an unsigned char), so we end up with something like octets[0] & mask[0]. Thanks to C's type promotion rules, even if both operands are unsigned chars, the result is typically going to be int. We need the result to be an unsigned char though (i.e., one octet) not an int (typically 4 octets) though. As such, in this situation, auto would almost certainly be inappropriate.

That still leaves a lot of cases where it's a judgement call though. My own tendency for these cases is to treat auto as the default, and only use an explicit type in cases that are at least a little like the latter case I've cited above -- even if a particular type isn't needed for correct operation that I really want a particular type, even if that might involve an implicit conversion.

My guess (but it is just a guess) is that over time, I'll probably tend even more in that direction. As I get more accustomed to the compiler picking out types, I'll find that a fair number of cases where I currently think I should specify the type, I really don't need to and the code will be just fine.

I suspect a lot of us (and the older/more experienced we are, probably the worse we'll be about it) will use explicit types for reasons that ultimately trace back to some feeling about performance, and believing that our choice will improve performance. Part of the time we may even be right -- but as most of us with that much experience have found, our guesses are often wrong (especially when they're based on implicit assumptions), and compilers and processors generally get better at such things over time as well.