2

我遇到了来自 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...() 的值是未定义的,还是编译器行为不端?

4

1 回答 1

10

这听起来很像旧的可变参数模板仿真和真正的可变参数模板之间的不匹配。

在可变参数模板出现之前,人们可以使用有限数量的默认模板参数来模拟它们:

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 的原因:该包中确实有 10 种类型,即使其中 8 种只是我们决定赋予“此处无类型,继续前进”含义的标记。

编译器不知道我们的约定,所以它计算一切。tuple_size知道这个约定并且只计算我们关心的实际类型。

于 2012-08-09T02:47:26.370 回答