这是使用sizeof ...()运算符的错误吗?

时间:2012-08-09 02:32:41

标签: c++ compiler-construction sizeof variadic-templates

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

1 个答案:

答案 0 :(得分:9)

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

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

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>等,最多可以写入10个参数。

然后可以通过计算第一个tuple_size之前的模板参数数量来实现

void_。功能集的其余部分也以类似的方式实现。

可悲的是,这个技巧与真正的可变参数模板相互作用很差。给定tuple<int, double>时,tuple<Args...>上的类型推导将推导Args包为{int,double,void_,void_,void_,void_,void_,void_,void_,void_}。这就是sizeof...(Args)返回10的原因:该包中确实有十种类型,即使其中八种只是标记,我们决定赋予“没有类型看这里,移动”的含义。

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