The 2011 C++ standard introduced the new keyword auto
, which can be used for defining variables instead of a type, i.e.
auto p=make_pair(1,2.5); // pair<int,double>
auto i=std::begin(c), end=std::end(c); // decltype(std::begin(c))
In the second line, i
and end
are of the same type, referred to as auto
. The standard does not allow
auto i=std::begin(container), e=std::end(container), x=*i;
when x
would be of different type. My question: why does the standard not allow this last line? It could be allowed by interpreting auto
not as representing some to-be-decuded type, but as indicating that the type of any variable declared auto
shall be deduced from its assigned value. Is there any good reason for the C++11 standard to not follow this approach?
There is actually a use case for this, namely in the initialisation statement of for
loops:
for(auto i=std::begin(c), end=std::end(c), x=*i; i!=end; ++i, x+=*i)
{ ... }
when the scope of the variables i
, end
, and x
is limited to the for
loop. AFAIK, this cannot be achieved in C++ unless those variables have a common type. Is this correct? (ugly tricks of putting all types inside a struct
excluded)
There may also be use cases in some variadic template applications.
I think it's just a matter of consistency with non-auto
declarations.
This:
auto n = 42, *p = &n;
is equivalent to:
int n = 42, *p = &n;
where the types int
and int*
are derived from the initializers. In both cases, even though int
and int*
are different types, they're permitted to be in the same declaration because of their close syntactic relation. (By the "declaration follows use" rule that C and C++ declarations almost follow, you're defining both n
and *p
as being of type int
.)
It would have been possible to permit unrelated types in the same declaration:
auto n = 42, x = 1.5;
but the above would have to be equivalent to two separate declarations:
int n = 42; double x = 1.5;
I think the idea when adding auto
was to make a minimal change to the language, permitting the type to be inferred from an initializer but not changing the kinds of declarations that are possible.
Even without auto
, you could define an int
and an int*
in a for
loop header:
for (int n = 42, *p = &n; expr1; expr2) { /* ... / }
but you couldn't declare an int
and a double
together. The addition of auto
simply didn't change that.
Outside the context of a for
loop, it's generally much better to use separate declarations anyway. Shoving a lot of different declarations into a for
loop is arguably a bad idea in most cases. And for the (probably rare) cases where you want a lot of declarations, you can just put them above the loop, something like this:
auto i=std::begin(c), end=std::end(c),
for( x=*i; i!=end; ++i, x+=*i) {
// ...
}
adding another set of {
}
around the whole thing if you want to limit the scope. (In this case, you'd probably want end
to be const
anyway.)
According to the final revision of the accepted proposal for this feature, N1737,a possible multi-declarator auto
implementation is as follows: (from Section 6)
We believe it is possible to achieve both consistent form and consistent behavior. We do so by inserting (for purposes of exposition) an intermediate __Deduced_type definition, and applying this type consistently in the as-if expansion:
// Listing 12
typedef int __Deduced_type; // exposition only
__Deduced_type a = 1;
// decltype(a) is int
__Deduced_type b = 3.14; // decltype(b) is int
__Deduced_type * c = new float; // error; decltype(c) would be int *
Not only do we achieve consistency of both form and behavior via such a reconciled formulation, we also address more complicated situations. For example, when the leading declarator includes a ptr-operator:
// Listing 13
auto * a = new int(1), b = 3.14, * c = new float;
our formulation attaches semantics as-if declared:
// Listing 14
typedef int __Deduced_type; // exposition only
__Deduced_type * a = new int(1); // decltype(a) is int *
__Deduced_type b = 3.14; // decltype(b) is int
__Deduced_type * c = new float; // error; decltype(c) would be int *
As shown by this possible implementation, changing the type would be invalid, and as such would cause an error.
This feature was implemented this way because otherwise it would be different than other types of multi-variable declarations.
I remember seeing a discussion about whether or not to allow changing types, but I cannot remember where. IIRC, they decided that it would be harder to implement, but another reason could be that it was dropped because they could not come to a consensus on when to choose different types (the new behavior) or when to implicitly convert to the deduced type of the first declared variable (the old behavior).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With