Code in an if (blah) {
block }
is compiled and must be valid even if the condition blah
is false.
template<bool b>
using bool_t = std::integral_constant<bool, b>;
template<int I, typename ... Tlist>
void print(std::ostream& s, std::tuple<Tlist...> const& t, std::false_type) {
// no more printing
}
template<int I, typename ... Tlist>
void print(std::ostream& s, std::tuple<Tlist...> const& t, std::true_type) {
s << std::get<I>(t) << ", ";
print<I+1>(s, t, bool_t<((I+1) < sizeof...(Tlist))>{});
}
template<typename ... Tlist>
std::ostream& operator<<(std::ostream& s, std::tuple<Tlist...> const& t)
{
print<0>(s,t, bool_t<(0 < sizeof...(Tlist))>{});
return s;
}
should work. Here we use tag dispatching to control which overload we recursively call: the 3rd argument is true_type
if I
is a valid index for the tuple, and false_type
if not. We do this instead of an if
statement. We always recurse, but when we reach the end of the tuple, we recurse into the terminating overload.
live example
As an aside, it is ambiguous if overloading <<
for two types defined in std
is compliant with the standard: it depends if std::tuple<int>
is a "user defined type" or not, a clause that the standard does not define.
On top of that, it is considered best practice to overload operators for a type within the namespace of that type, so it can be found via ADL. But, overloading <<
inside std
is illegal under the standard (you cannot inject new overloads into std
). The result can be somewhat surprising behavior in some cases, where the wrong overload is found, or an overload is not found.