我在 clang++ 编译器中发现了 sizeof...() 运算符的奇怪行为:
template <typename... Args>
void test(tuple<Args...> t) {
cout << "size: " << sizeof...(Args) << endl;
}
...
test(make_tuple(1, 2)); // prints 'size: 10'
我意识到更标准的方法是:
template <typename... Args>
void test(tuple<Args...> t) {
cout << "size: " << tuple_size<tuple<Args...> >::value << endl;
}
test(make_tuple(1, 2)); // prints 'size: 2'
但我仍然很好奇为什么我得到第一个版本的奇怪值。在这种情况下, sizeof...() 的值是否未定义,或者编译器行为不当?
这听起来很像旧的可变参数模板模拟和真正的可变参数模板之间的不匹配。
在可变参数模板出现之前,人们可以使用有限数量的默认模板参数来模拟它们:
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 */ };
这允许您写入
tuple<int>
和 tuple<int, double>
等等,最多十个参数。
然后可以通过计算第一个 tuple_size
之前的模板参数数量来实现
void_
。功能集的其余部分以类似的方式实现。
遗憾的是,这个技巧与真正的可变参数模板的交互效果很差。当给定
tuple<int, double>
时,tuple<Args...>
上的类型推导会将 Args
包推导为 { int, double, void_, void_, void_, void_, void_, void_, void_, void_ }。这就是为什么 sizeof...(Args)
返回 10:该包中确实有十种类型,即使其中八种只是我们决定分配“这里没有类型可看,继续前进”含义的标记。
编译器不知道我们的约定,所以它会计算一切。
tuple_size
了解这个约定,并且只计算我们关心的实际类型。