函数和继承中的非类型模板参数

时间:2015-01-22 14:49:41

标签: c++ variadic-templates variadic-functions

我正在关注this精彩教程。作者大量使用可变参数模板,我来到了一个我陷入困境,无法理解的地步。你能救我吗?

1。为什么不编译?

// this is simple
template<size_t I, typename T>
struct tuple_element
{
    T value;
};

// this does NOT compiles: error: parameter pack 'Indices' must be at the end of the template parameter list
template <size_t... Indices, typename... Types>
struct tuple_impl : tuple_element<Indices, Types>...
{
};

接下来,作者有这个编译好的代码:

template <size_t... Indices>
struct index_sequence
{
    using type = index_sequence<Indices...>;
};

template <typename Sequence, typename... Types>
struct tuple_impl;

template <size_t... Indices, typename... Types>
struct tuple_impl<index_sequence<Indices...>, Types...>
  : tuple_element<Indices, Types>...
{
};

2。为什么在这种情况下一切正常?我在这里看到几乎相同的模式:tuple_element<Indices, Types>...

第3。为什么不能编译:

template <size_t... Indices>
void g(Indices...){}; //error: variable or field 'g' declared void 

3 个答案:

答案 0 :(得分:2)

您看到的错误在于传递参数。这是一个编译错误:

template <size_t... Indices, typename... Types>
struct tuple_impl
{};

live example

规则是你不能在模板类模板参数列表中有一个包后跟另一个包。

第二个示例是特化,其中包规则不存在。专门化的模板参数仅仅是从与主模板匹配的模式中提取的类型和值,由类型名称后的专业化的<>部分指导。

因为它们从未在同一列表中传入顺序,有一个...一个接一个,不会导致任何歧义。使用主要模板时,顺序很重要,...之后的任何内容都很难与...中的更多内容区分开来。可能为了使编译器的工作更容易,即使在它不能含糊不清的情况下(例如,一包文字后跟一组类型),C ++也禁止使用它。

答案 1 :(得分:1)

  1. 编译器无法区分第一个序列何时结束和第二个序列何时开始 - 因此最后允许在可变参数模板中只有一个参数包。

    tuple_impl<a, b, c, d, e, f> //is d still an index or already a type? what about e?
    
  2. 这很有效,因为tuple_impl本身就是一个只有一个参数包的模板Types...。它恰好发生在这个专业化中,第一个参数也是一个模板,它也有一个参数包。因此,与一个包含两个包的模板相比,您有两个模板,每个模板一个包,这没关系。

  3. 这与可变参数模板无关,即出于同样的原因,它也不适用于单个参数。事实是,由于Indices...不是类型而是值,因此您没有定义函数。如果它不是无效的,编译器以后会遇到问题。考虑这个例子,它稍作修改,但基本上是一个类似的结构:

    template <size_t I>
    unsigned g(I)
    {}
    
  4. 中间行是中心:编译器认为它是一个变量定义,用I初始化。因此,您的案例中的错误,因为void类型的变量根本没有意义。然后我的编译器发出关于模板的警告,它认为g是模板化变量,那些是C ++ 1y扩展。完成后,他意识到变量定义没有由;完成,发出错误并退出......

答案 2 :(得分:1)

只是添加一些重要的事情:这是一个测试片段:

int main( int argc, char** argv )
{
    using i3 = index_sequence<1, 2, 3>;
    tuple_impl<i3, int, double, char> tup;

    return 0;
}

注意:在这里,您将此i3作为“索引包”传递。 “主模板”始终定义必须如何将参数传递给模板。如果为特化设置template<...>语句,则不会定义任何内容,而是参数组合内部可以在专门化内部传播的内容,但它不是公共接口的一部分。

例如,如果您尝试使用<1, 2, 3, int, double, char>作为参数规范(理论上它与特殊化的模板参数规范匹配),则无法编译。