Now, I still don't understand why the compiler can't just do that ... |
It probably
could (if the standard specified how it should work) but it's unusual to have such special rules for a particular library feature. It's usually better to come up with more general language features that can be used by more code otherwise you risk ending up with a very inconsistent language that is hard to update because with any new language feature that is introduced they must be very careful to not break any of these small little "hacks" that seemed like a good idea at the time.
... when it can create variadic recursive functions during compile time ... |
Variadic templates is a language feature that has many use cases. It was not designed with only one particular library feature in mind.
...can do many things including take the first line below and really write out the 2nd line ...
1 2
|
tuple<int, char, string> tup1(101, 's', "Hello Tuple!");
tuple<int, char, string> tup1(make_tuple(101, 's', "Hello Tuple!"));
|
|
It doesn't. There is no magic going on here. Just normal constructors and function calls.
The first line passes the arguments directly to the constructor.
The second line uses a function that will call the constructor internally and return the tuple.
The purpose of make_tuple is to be able to deduce the template arguments,
so instead of having to write
|
std::tuple<int, double, bool>(1, 2.5, true);
|
you can write
|
std::make_tuple(1, 2.5, true);
|
C++17 introduced
class template argument deduction (CTAD) so now you can write
|
std::tuple(1, 2.5, true);
|
so I'm not sure how useful make_tuple is any more.
Here "get<numMembers - 1>" is NOT a constant, because I can refer to index 0,1, and 2. |
The expression that you pass between
< and
> has to be a compile time constant. That means
numMembers-1
needs to be a
constant expression (an expression that can be evaluated to a constant value at compile time) otherwise you'll get a compilation error. This is based on language rules and is not affected by compiler optimizations and things like that.
1 2
|
int x = 0;
std::cout << std::get<x>(tup) // error because x is not a compile time constant.
|
The reason I said
compile time constant and not just
constant is to make it clear it's not always enough to declare a variable with the const keyword. Sometimes people use the word
runtime constant to refer to const variables that are not compile time constants.
1 2 3 4
|
int y;
std::cin >> y;
const int x = y;
std::cout << std::get<x>(tup) // still an error
|
Inside DisplayTupleInfo from my original code, (tupleType& tup) is not declared as a constant, it is a reference to the three heterogenous variables. |
Whether the tuple is constant or not doesn't matter. That's not what we're discuss here. It is the template arguments (what you write between
< and
>) that has to be constant. That's always the case and necessary for the way templates work.
When I am inside the DisplayTupleInfo function I can get<0>. get<1>, get<2> references to the type all day long |
Note that get<0>(tup), get<1>(tup) and get<2>(tup) is essentially calling three different functions (with different return types).
Also note that std::get has additional template arguments that gets deduced from the tuple argument so calling get<0> on a std::tuple<int, double> and on std::tuple<float, bool> is technically calling two different functions. That's what allows it to have different return types (int& and float&).
even though the tuple is written into the object (as if it were the members) we do not loose that constant TYPE reference. The compiler somehow connects them in "tup" reference. |
I'm having a hard time decipher this.
A tuple
is an object.
std::tuple<int, double> is a different type than std::tuple<float, bool>.
So now all of a sudden then I want to:
|
cout << "Last element: " << tup[numMembers - 1] << endl;//NON-WORKING THEORETICAL
|
...And I lose the "tup" constant reference, where before I had it with:
|
cout << "Last element: " << get<numMembers - 1>(tup) << endl;
|
|
According to the current language rules what's written inside
[] (or
()) are not required to be constant,
but what's written inside
<> always has to be constant. That's a big mismatch.
In this case you need the
INDEX argument (not the tuple itself) to be constant (and it needs to be usable as a constant in the implementation) but there is currently no way for the
[] operator to enforce that.