I came across what looks like strange behavior from the clang++ compiler for the sizeof...() operator:
template <typename... Args>
void test(tuple<Args...> t) {
cout << "size: " << sizeof...(Args) << endl;
}
...
test(make_tuple(1, 2)); // prints 'size: 10'
I realize that the more standard method would be:
template <typename... Args>
void test(tuple<Args...> t) {
cout << "size: " << tuple_size<tuple<Args...> >::value << endl;
}
test(make_tuple(1, 2)); // prints 'size: 2'
But I'm still curious as to why I get the weird values for the first version. Is the value of sizeof...() undefined for this case, or is the compiler misbehaving?
This sounds a lot like a mismatch between old variadic template emulation and true variadic templates.
Before variadic templates came along, people could emulate them with a finite number of defaulted template parameters:
struct void_ {}; // marker type not used anywhere else
template <typename T0 = void_,
typename T1 = void_,
typename T2 = void_,
typename T3 = void_,
typename T4 = void_,
typename T5 = void_,
typename T6 = void_,
typename T7 = void_,
typename T8 = void_,
typename T9 = void_>
struct tuple { /* blah blah magic here */ };
This allows one to write tuple<int>
, and tuple<int, double>
, and so on, up to ten parameters.
tuple_size
can then be implemented by counting the number of template parameters before the first void_
. The rest of feature set is implemented in a similar vein.
Sadly, this trick interacts quite poorly with true variadic templates. When given tuple<int, double>
, the type deduction on tuple<Args...>
will deduce the Args
pack to be { int, double, void_, void_, void_, void_, void_, void_, void_, void_ }. This is why sizeof...(Args)
returns 10: there are indeed ten types in that pack, even if eight of them are just markers that we decided to assign the meaning of "no type to see here, move along".
The compiler doesn't know about our convention, so it counts everything. tuple_size
knows about this convention and counts only the actual types we care about.