Sometimes you need to bundle things together so you can treat them as a single unit. That’s what the tuple provides.
You might wonder why not just use a container to put things in? Containers are good at storing as many instances of some type as you want. But everything that goes into a container has to be the same type. Sure, you can put pointers in a container and then have different specific derived types in the container. But the container still only holds pointers of a specific type.
A tuple allows you to put completely unrelated types inside. It does this by defining the number and type of each item the tuple can hold. This means you can’t just put whatever you want into any tuple. But you can declared a custom tuple to hold whatever you want. Sometimes you only need to store two things. This is usually called a pair and has been around for quite a while. It allows you to store both a key and a value, for example, as needed by a dictionary.
You shouldn’t use a pair or a tuple to hold instances of types like a general purpose collection. If we take the backpack example from an adventure game, then you wouldn’t want to use a pair to describe the backpack contents. That would mean that the backpack could only hold two items. But you also wouldn’t want to use a tuple either even though a tuple can hold as many items as you want. A tuple can hold as many things and of whatever type you want but this has to be defined at compile time. For a backpack, the best solution is to keep using a collection class and store pointers or interfaces to some common aspect that all items will share. This allows you to treat each item in the backpack the same and then the hero can store however many items needed at runtime.
Anytime you’re creating code that needs to work with a group of unrelated types and you don’t know ahead of time what those types are or what they mean, then that’s when you can use a tuple.
Listen for more on tuples or you can also read the full transcript below.
Transcript
You might wonder why not just use a container to put things in? Containers are good at storing as many instances of some type as you want. But everything that goes into a container has to be the same type. Sure, you can put pointers in a container and then have different specific derived types in the container. But the container still only holds pointers of a specific type.
A tuple allows you to put completely unrelated types inside. It does this by defining the number and type of each item the tuple can hold. This means you can’t just put whatever you want into any tuple. But you can declared a custom tuple to hold whatever you want.
You might hear some people refer to a tuple as a toople. I call them tuples.
Sometimes you only need to store two things. This is usually called a pair and has been around for quite a while. It allows you to store both a key and a value, for example, as needed by a dictionary. Check out episode 44 for more information about dictionaries.
Let me first say that you shouldn’t use a pair or a tuple to hold instances of types like a general purpose collection. If we take the backpack example from an adventure game, then you wouldn’t want to use a pair to describe the backpack contents. That would mean that the backpack could only hold two items. But you also wouldn’t want to use a tuple either even though a tuple can hold as many items as you want. A tuple can hold as many things and of whatever type you want but this has to be defined at compile time. For a backpack, the best solution is to keep using a collection class and store pointers or interfaces to some common aspect that all items will share. This allows you to treat each item in the backpack the same and then the hero can store however many items needed at runtime.
You use a pair or a tuple when you have a specific set of types that you want to group together. I’ll continue the explanation right after this message from our sponsor.
Let’s say you have a marketplace in a game that allows the hero to purchase items. You could use a dictionary that allows you to store and look up information about inventory items based on their name. This information could include such things as how many pieces of that item the shopkeeper currently has, the price, and a description, along with a name of course. You could create a struct to hold these four attributes, count, price, description, and name. Then the dictionary which uses templates would be declared to hold keys of type string that matches the type of the name and values of the struct type you defined to hold the four pieces of information. Whether or not you want to include the name in the struct is up to you. It’s a bit redundant since the name would be held twice in the dictionary, once as the key and again in the value. But the point is that the dictionary really only knows about two types. And it always knows about two types. And the type of these two types is fixed at compile time when the dictionary was declared. This is a great example of when to use a pair. And that’s exactly what most dictionaries use.
So when would you use a tuple? In a way, we just did. You can think of a pair as a special case of a tuple designed for exactly two items. You access the first item through a method called first and the second item through a method called second.
But notice how I explained how to use a custom struct to hold the four pieces of information. That struct would probably have methods named specifically for the value stored. I mean, that the struct would probably access the number of items on-hand with a method called count. The price would be made available through a method called price, etc.
You could use a tuple for this instead of a struct but then you’d lose those custom method names. You’d have to get to the price through a template method called get angle bracket zero angle bracket. Notice that the tuple doesn’t provide names like first and second. It’s a type that can hold potentially many items so instead provides access through numbers instead. The first item in a tuple is at position 0. The second item is at position 1. Etc.
You can’t have a single method called get that takes an argument with the number of the item to obtain. This is because the tuple is strongly typed. Maybe the first item is of type int, the second item is also of type int, the third item is of type string, and the fourth item is of type string. That would match the structure I explained earlier to hold the inventory information. The reason there can’t be a single method called get that takes a number identifying the item to return is because then the return type of the method would have to change. And you can’t do that in a strongly typed language. The only way to accomplish this is to make the get method a template itself and then the number forms part of the method signature. So get<0> is a completely different method than get<1> and each can be declared through the template ability of the language to have a return type that matches the type in the tuple at that location.
It comes down to this. If you have a bunch of types that you want to treat as a bundle, then you could declare your own struct that wraps them all up and allows you to create custom names for each part. Or you could declare a tuple and get all the same strongly typed access that the struct provides but lose the custom names. Why would you ever want to use a tuple then?
When it’s your code involved like in the struct, then I’d say to avoid a tuple. But anytime you’re creating code that needs to work with a group of unrelated types and you don’t know ahead of time what those types are or what they mean, then that’s when you can use a tuple. Maybe you need to write some code that allows another caller to send you a group of items and you want to treat them as a single unit. You also don’t know what uses the other code will try to apply your code so have no idea what the types will be or how many.
If you don’t know what the data means, then referring to the individual parts by number is just as good as anything.