I usually create custom structs
when grouping values of different types together. This is usually fine, and I personally find the named member access easier to read, but I wanted to create a more general purpose API. Having used tuples extensively in other languages I wanted to return values of type std::tuple
but have found them much uglier to use in C++ than in other languages.
What engineering decisions went into making element access use an integer valued template parameter for get
as follows?
#include <iostream>
#include <tuple>
using namespace std;
int main()
{
auto t = make_tuple(1.0, "Two", 3);
cout << "(" << get<0>(t) << ", "
<< get<1>(t) << ", "
<< get<2>(t) << ")\n";
}
Instead of something simple like the following?
t.get(0)
or
get(t,0)
What is the advantage? I only see problems in that:
Edit: I've accepted an answer. Now that I've thought about what needs to be known by the language and when it needs to be known I see it does make sense.
The second you've said:
It makes indexing by runtime generated indices difficult (for example for a small finite ranged index I've seen code using switch statements for each possibility) or impossible if the range is too large.
C++ is a strongly static typed language and has to decide the involved type compile-time
So a function as
template <typename ... Ts>
auto foo (std::tuple<Ts...> const & t, std::size_t index)
{ return get(t, index); }
isn't acceptable because the returned type depends from the run-time value index
.
Solution adopted: pass the index value as compile time value, so as template parameter.
As you know, I suppose, it's completely different in case of a std::array
: you have a get()
(the method at()
, or also the operator[]
) that receive a run-time index value: in std::array
the value type doesn't depends from the index.
The "engineering decisions" for requiring a template argument in std::get<N>
are located way deeper than you think. You are looking at the difference between static and dynamic type systems. I recommend reading https://en.wikipedia.org/wiki/Type_system, but here are a few key points:
In static typing, the type of a variable/expression must be known at compile-time. A get(int)
method for std::tuple<int, std::string>
cannot exist in this circumstance because the argument of get
cannot be known at compile-time. On the other hand, since template arguments must be known at compile-time, using them in this context makes perfect sense.
C++ does also have dynamic typing in the form of polymorphic classes. These leverage run-time type information (RTTI), which comes with a performance overhead. The normal use case for std::tuple
does not require dynamic typing and thus it doesn't allow for it, but C++ offers other tools for such a case.
For example, while you can't have a std::vector
that contains a mix of int
and std::string
, you can totally have a std::vector<Widget*>
where IntWidget
contains an int
and StringWidget
contains a std::string
as long as both derive from Widget
. Given, say,
struct Widget {
virtual ~Widget();
virtual void print();
};
you can call print
on every element of the vector without knowing its exact (dynamic) type.
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