我正在关注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
答案 0 :(得分:2)
您看到的错误在于传递参数。这是一个编译错误:
template <size_t... Indices, typename... Types>
struct tuple_impl
{};
规则是你不能在模板类模板参数列表中有一个包后跟另一个包。
第二个示例是特化,其中包规则不存在。专门化的模板参数仅仅是从与主模板匹配的模式中提取的类型和值,由类型名称后的专业化的<>
部分指导。
因为它们从未在同一列表中传入顺序,有一个...
一个接一个,不会导致任何歧义。使用主要模板时,顺序很重要,...
之后的任何内容都很难与...
中的更多内容区分开来。可能为了使编译器的工作更容易,即使在它不能含糊不清的情况下(例如,一包文字后跟一组类型),C ++也禁止使用它。
答案 1 :(得分:1)
编译器无法区分第一个序列何时结束和第二个序列何时开始 - 因此最后允许在可变参数模板中只有一个参数包。
tuple_impl<a, b, c, d, e, f> //is d still an index or already a type? what about e?
这很有效,因为tuple_impl
本身就是一个只有一个参数包的模板Types...
。它恰好发生在这个专业化中,第一个参数也是一个模板,它也有一个参数包。因此,与一个包含两个包的模板相比,您有两个模板,每个模板一个包,这没关系。
这与可变参数模板无关,即出于同样的原因,它也不适用于单个参数。事实是,由于Indices...
不是类型而是值,因此您没有定义函数。如果它不是无效的,编译器以后会遇到问题。考虑这个例子,它稍作修改,但基本上是一个类似的结构:
template <size_t I>
unsigned g(I)
{}
中间行是中心:编译器认为它是一个变量定义,用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>
作为参数规范(理论上它与特殊化的模板参数规范匹配),则无法编译。