You could delete the first overload and the effect would be the same. The first overload is never a candidate because it takes a tuple
with only one T
.
Looking at the second overload, the problem is that you have the wrong order of the template parameters, namely the size_t
and the parameter pack T...
. The compiler needs to have the parameter pack at the end. The rules for this are basically:
- You can only have one parameter pack which is explicitly specified.
- This must be the last explicitly specified parameter.
- All other parameters (if any) must be deduced from the arguments or they must have a default.
Here's something that allows the recursive call itself, due to swapping the parameters, but now the compiler itself will end up with an endless recursion:
#include <iostream>
#include <tuple>
using namespace std;
template<size_t N, typename ...T>
ostream & print (ostream& stream, tuple<T...> const & t)
{
print< N-1, T... > (stream, t);
return stream << get<N-1>(t) << endl;
}
template<typename ...T>
ostream & operator << (ostream& stream, tuple<T...> const & t)
{
return print< sizeof...(T), T... >( stream, t );
}
int main ()
{
auto t = make_tuple ("hallo", 1L, 1.1);
cout << t << endl;
}
You can try it out (nothing really bad will happen) and after some time, you'll receive an error message from the compiler.
To illustrate the problem further, look at this reduced example:
#include <tuple>
// everything after the first parameter pack *must* be deduced or come from a default
template<typename ...T, int S=0>
void f(const std::tuple<T...>& arg) {}
int main() {
// OK
f(std::tuple<int>(1));
f<int>(std::tuple<int>(1));
// Fail
f<int,0>(std::tuple<int>(1));
}