The new keyword "auto"; When should it be used to declare a variable type? [duplicate]
Possible Duplicate:
How much is too much with C++0x auto keyword
Have we (as a community) had enough experienc开发者_如何学Ce to determine when and/or whether auto is being abused?
What I am really looking for is a best practices guide on
- when to use auto
- when it should be avoided
Simple rules of thumb that can quickly be followed in 80% of cases.
As a context this question is sparked by my response here
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 new
1 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.
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
- in most cases it’s easy to see whether an explicit type needs to be known, and
- 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 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 char
s, 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.
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.
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.
精彩评论