I would imagine you see tuples often in code. I need to get this off my chest but correct me if I am mistaken in thinking the syntax should be better? But the syntax and function calls are all over the place! |
I have to admit that I don't use tuples very often. I think they are mostly useful in templated code when you need to store a bunch of values that you don't really know what they represent (e.g. if you want to store the arguments of a variadic function template in order to pass them to a function at a later time such as std::function might do internally). In such codes the use of tuple would probably not be the only thing that makes the code look complicated.
In normal situations I would just use a struct/class with named members if possible.
1st lines are the original and 2nd line is what I would change them to.
1 2
|
const int numMembers = tuple_size<tupleType>::value;
const int numMembers = tuple_size<tupleType>::size; //NON-WORKING THEORETICAL
|
|
It's a common convention that the standard uses. Look at the type traits.
https://en.cppreference.com/w/cpp/header/type_traits
If the class template is used to compute a type then it has a member named
type.
If the class template is used to compute a value then it has a member named
value.
Your preferred approach would lead to a lot of word repetition.
1 2
|
tuple_size<tupleType>::size
~~~~ ~~~~
|
And it's arguably a bit misleading too because the member does not give us the size of the tuple_size<tupleType> object.
Of course I would have loved it even better if it was not a helper class and just a function within the tuple class even more.... |
Despite the name, tuple_size is not just for std::tuples. You can use it on std::pair and std::array too. And if you create your own type that contains a fixed number of elements you can specialize it for that type also. This allows you to treat all tuple-like objects the same and as others have mentioned it allows you to use the type in structured bindings.
https://en.cppreference.com/w/cpp/language/structured_binding
In general the standard library prefers to keep customization points like this as non-members. This way you can add the customization to non-class types and to class types that you cannot change for whatever reason. It also avoids any name clashes with the members in the class.
1 2
|
cout << "Last element: " << get<numMembers - 1>(tup) << endl;
cout << "Last element: " << tup[numMembers - 1] << endl;//NON-WORKING THEORETICAL
|
|
Don't forget that the values stored in a tuple are not necessarily of the same type. The subscript operator
[] works essentially like a function (it can be overloaded). It takes a runtime value as argument and returns a runtime value of a fixed type.
If x in tup[x] is not required to be a compile time constant then how would the compiler know what type the expression returns? The answer is that it would generally be impossible and that is a big problem in a statically-typed language like C++.
If x in tup[x] was required to be a compile time constant then the rules of the language need to change. We would essentially need
constexpr parameters which has been proposed but I'm not sure how that will progress.
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1045r1.html
Look at the before and after examples in that proposal and you'll find your suggested syntax there, so maybe we will be able to use this syntax one day, who know...
1 2
|
make_tuple(101, 's', "Hello Tuple!")
tuple_make(101, 's', "Hello Tuple!") //NON-WORKING THEORETICAL
|
Keep the order consistent. |
When make_tuple was added we already had make_pair so calling it tuple_make would have been inconsistent.
If you mean the order should have been consistent with tuple_size I think you will have to blame the English language.
tuple_size<T> represents the "tuple size" of T. And to get its value we use ::value. It makes total sense.
make_tuple is a function template that we use to
make a
tuple. We don't
tuple make them, do we?
I'm not saying pair_make/tuple_make would necessarily have been bad. There are pros and cons to both naming schemes. All I'm saying is that make_pair and make_tuple are the most intuitive names and what most programmers would choose unless they had been convinced the reverse is better. Since they have started to name things this way they would now need an even more convincing argument to change to a different naming scheme for new similar functions.