8

Why is this not allowed, for example:

std::array<auto, 5> myArray {

};

It would make my life so much easier, as I would be allowed to store multiple data-types inside the array. I'm sure there's a logical explanation, just wondered what it was.

Cassio Neri
  • 19,583
  • 7
  • 46
  • 68
Phorce
  • 2,632
  • 13
  • 43
  • 76
  • 27
    "as I would be allowed to store multiple data-types" No, it would not. `auto` is not dynamic typing. – R. Martinho Fernandes Sep 09 '13 at 15:09
  • 1
    `auto` does not allow different types. – Xeo Sep 09 '13 at 15:09
  • 1
    This, right here, is why `auto` was a bad idea. I predicted this misconception four years ago. – Lightness Races in Orbit Sep 09 '13 at 15:16
  • Try doing `auto var = 5;` and then assigning a class/struct like `std::complex` to it. You should quickly see that no, you can't store multiple types in `var`. – Cornstalks Sep 09 '13 at 15:17
  • 3
    I -1'd for a moment, because the misconception is quite sad. But I removed the downvote because than in itself is (a) not your fault, and (b) not a reason to consider this as a bad question. – Lightness Races in Orbit Sep 09 '13 at 15:18
  • 2
    @LightnessRacesinOrbit: I think this misconception is pretty minor, as it's only had by people first learning C++ (or about `auto`), and is quickly explained away. Thus, I'm not sure this is a *major* con for `auto`. – Cornstalks Sep 09 '13 at 15:19
  • @LightnessRacesinOrbit hardly a difficult prediction: the same thing happened with C# about five years ago. – R. Martinho Fernandes Sep 09 '13 at 15:21
  • 2
    We need `dynamic` in C++ :) – sehe Sep 09 '13 at 15:21
  • @R.MartinhoFernandes: Spoilsport. – Lightness Races in Orbit Sep 09 '13 at 16:03
  • @Cornstalks: There are certainly worse ones. – Lightness Races in Orbit Sep 09 '13 at 16:04
  • @LightnessRacesinOrbit Yet I'm not sure this misconception is really a big problem. That's one of the very few instances of it I've ever seen of it. A prediction that happens once every four years (exaggerating!) is not a bad one, but also not one that's gonna change the world. Of course people not understanding the very basics of C++'s type system will probably fall for that *once*, but well, then that's probably not their worst problem anyway. I agree that *Herb Sutter*'s approach of *"almost always auto"* is a bit extreme, yet I don't want to miss `auto` either. – Christian Rau Sep 10 '13 at 07:25
  • @ChristianRau: I did not say "once every four years". I said "four years ago", and I was not exaggerating. – Lightness Races in Orbit Sep 10 '13 at 07:54
  • @LightnessRacesinOrbit No, *I* was exaggerating, since I knew that you *didn't* say *"once every four years"*, *I* said it, and it was an *exaggeration on my side*, since I guess it happens more often than *"once every four years"*. I never doubted that you made this prediction *"four years ago"* and weren't exaggerating with that. Sorry for the confusion. – Christian Rau Sep 10 '13 at 07:55

3 Answers3

12

auto is used to deduce one type from an expression. Using your suggested syntax would not help because exactly one type can be stored in a container. If you need a way to store any type in the container, take a look at boost::any, so you can use

std::array<boost::any, 5> myArray;
cdmh
  • 3,294
  • 2
  • 26
  • 41
  • So... by your explanation we can expect the new question: _"Why doesn't `std::array arr { 1,2,3,4,5 };` work, while `auto arr[] = { 1,2,3,4,5 };` works as expected? – sehe Sep 09 '13 at 15:19
  • In `auto arr[]={1,2,3,4,5};`, `auto` will deduce the type, which is a C style array. You cannot use `auto` as a template parameter because the compiler cannot deduce the type, even if every element is the same so there is only one type. – cdmh Sep 09 '13 at 15:24
  • 2
    @cdmh: You didn't really explain it. You just repeated the assertion as its own explanation. – Lightness Races in Orbit Sep 09 '13 at 16:04
  • @sehe `{ 1,2,3,4,5 }` is not an expression, so it has no type to deduce. You could ask the language "find me a type `T` such that `std::array arr { 1,2,3,4,5 }` is valid", but it is impractical to invert `template` type substitution: doing so in the general case can require solving the halting problem, which is impossible. For that to work, one would need a weaker `template` metaprogramming language, or some syntax or conventions to tell the language the "right" way to turn constructor arguments into a type: the second is actually being worked on for future C++ standardization I believe. – Yakk - Adam Nevraumont Sep 09 '13 at 20:03
  • @Yakk aww, you really didn't have to explain. I was using the Socratic method to point out sloppiness in the explanation. (And, seeing how well it works, I might ask you: If `{ 1,2,3,4,5 }` is not an expression, then surely `std::vector v = { 1,2,3,4,5 };` must involve _aggregate initialization_, don't you agree :/ - I kid, only half though...) – sehe Sep 09 '13 at 21:25
  • @Yakk Thanks for your thoughtful angles. Yeah, C++ will probably get some more type magic in the near future. That said, `std::array arr { 1,2,3,4,5 };` would just work in, say, Haskell; there is no real reason it couldn't be made practical. As such, the answer should _really_ have explained in terms of language rules (_deducible context_) – sehe Sep 09 '13 at 21:27
  • @sehe `foo x = { 1, 3, 4 }`, where `foo` has a constructor that takes a `std::initializer_list`, where `U` is an arbitrary turing-complete computation from the type `T`. How do you determine, in this general case, which `T` is the one wanted? C++ doesn't require that the mapping `foo`->types of constructors of `foo` be invertible, let along requires that such an inverse be described and practical. Possibly Haskell does, which makes this problem much, much easier, and equivalent to the signature matching problem with `template` functions in C++. – Yakk - Adam Nevraumont Sep 09 '13 at 21:39
  • @Yakk yeah, you reframed my Socratic q. to fit the OP's. However, I had no such intention. My point was merely `std::initializer_list` _is an expression_, AFAICT :/ _(I thought my "I kid" and other not-so-subtle hints would have been enough to warn you not to take it seriously. Sorry if I bothered you.)_ – sehe Sep 09 '13 at 21:51
  • this might be sloppy... @sehe sure but taking Socrates at face value is about the only way to tolerate that old sod. As for the expression bit: {} could be an initializer list or uniform initialization. We cannot tell until we know what is on the lhs of the `=`: so it is not an expression, just part of one. – Yakk - Adam Nevraumont Sep 09 '13 at 21:58
  • @Yakk Point conceded. Well done :/ (also, _lol_ at "old sod") – sehe Sep 09 '13 at 22:03
9

auto is not some sort of magical flexible data type that can store any type of data. auto is a mere compiler keyword that tells the compiler that it has to deduce the specific type automatically. The type is deducted at compile time, meaning that auto is implicitly replaced with a specific type. One specific type. There's no way auto would somehow help you to store different types of data in the same array.

And in order for the actual type to be deducible, the compiler must have enough information to deduce it. In your example the relationship between the auto and the data that must be used to perform deduction (the initializers in the {} list) is not known to the compiler, which is why auto does not work in this context.

For example (borrowing the example from the comments), when you write something like this

auto a[] = { 1, 2, 3, 4, 5 };

the entire declaration is built entirely from core language constructs. The compiler immediately knows that the values in {} are initializers for array elements, whose type is described by keyword auto. So, the meaning of auto is easy to define using the core language concepts.

But in a declaration like

std::array<auto, 5> myArray  = { 1, 2, 3, 4, 5 };

the template std::array is seen by the compiler proper as a user-defined data type. The relationship between the values in {} and template arguments is also user-defined and hidden inside the implementation of std::array. It can be arbitrarily complex. It is not even known whether such relationship exists. And this is why it generally not possible to derive the actual type of auto is such cases.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
7

Why is this not allowed

To allow that, you'd need a way for a template to specify how to infer template parameters from an object's initialiser. That would be quite a large and complicated change to the language, for little benefit.

It would make my life so much easier, as I would be allowed to store multiple data-types inside the array.

No, it wouldn't. An array can only contain a single type; all this would allow you to do is deduce that type from the initialisers (if they all had the same type), which is of limited use.

In general, auto represents a static type, inferred from the type of an expression. It sounds like you want either:

  • an ordered collection of objects of different static types. This is available in the C++11 standard library as std::tuple; or
  • an array of dynamically typed objects. There is no such thing (as yet) in the language or standard library; but boost::any or boost::variant will give you such a thing.
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Thanks for the reply. I understand that there is `boost::any` etc.. However, they would not allow me to do: `cout << foo[0];` and would have to re-cast each of the elements? – Phorce Sep 09 '13 at 16:53
  • 1
    @Phorce: `any` will need casting; `variant` or `tuple` elements can be streamed directly. If you need convenient dynamic types, then you may not have chosen the best language. – Mike Seymour Sep 09 '13 at 16:57