参数包必须位于参数列表的末尾...何时以及为何?

时间:2016-01-22 07:04:49

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

如果参数列表绑定到类,参数列表必须位于参数列表的末尾,如果参数列表是成员方法声明的一部分,则放宽约束。

换句话说,这个编译:

class C {
    template<typename T, typename... Args, typename S>
    void fn() { }
};

以下不是:

template<typename T, typename... Args, typename S>
class C { };

为什么第一种情况是正确的,第二种情况不是?
我的意思是,如果是合法的语法,那么两种情况都不应该是这样吗?

要清楚,真正的问题是我正在定义一个类似于以下类的类:

template<typename T, typename... Args, typename Allocator>
class C { };

将分配器类型作为最后一种类型将不胜感激,但我可以以某种方式解决它(无论如何,如果你有一个建议值得赞赏,也许你的优雅比我的优雅!!)。
那就是说,我收到了错误:

  

参数包'Args'必须位于模板参数列表的末尾

所以,我只是好奇地完全理解为什么它在某些情况下被接受,但它不在其他一些情况下。

Here是一个类似的问题,但它只是解释了如何解决问题,这对我来说非常清楚。

2 个答案:

答案 0 :(得分:16)

它对函数模板有效,但只有当参数推导可以帮助编译器解析模板参数时,因为它代表你的函数模板示例实际上是无用的,因为

template<typename T, typename... Args, typename S> void fn() { }
int main() { fn<int, int, int>(); }
test.cpp: In function 'int main()':
test.cpp:2:32: error: no matching function for call to 'fn()'
 int main() { fn<int, int, int>(); }
                                ^
test.cpp:1:57: note: candidate: template<class T, class ... Args, class S> void fn()
 template<typename T, typename... Args, typename S> void fn() { }
                                                         ^
test.cpp:1:57: note:   template argument deduction/substitution failed:
test.cpp:2:32: note:   couldn't deduce template parameter 'S'
 int main() { fn<int, int, int>(); }

编译器无法确定哪些模板参数属于参数包,哪些模板参数属于S。事实上就像@ T.C.指出它实际上应该是语法错误,因为以这种方式定义的函数模板不能被实例化。

更有用的功能模板类似于

template<typename T, typename... Args, typename S> void fn(S s) { }

现在编译器能够明确地将函数参数s与模板类型S匹配,副作用是S 总是推导出 - 第一个之后的所有显式模板参数都属于Args

这些都不适用于(主要)类模板,参数未被推断且明确禁止:

来自草案n4567

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf

[temp.param] / 11

  

[...]如果是主类模板或别名的 template-parameter   template是模板参数包,它应该是最后一个   模板参数。[...]

(如果推断出它们会在函数模板示例中模糊不清)。

答案 1 :(得分:11)

第一个是不对的。编译器只是错误并且无法诊断它。 [temp.param]/11

  

不得遵循功能模板的模板参数包   通过另一个模板参数,除非该模板参数可以   从函数模板的 parameter-type-list 中推导出来或者有一个   默认参数(14.8.2)。

如果函数类型T(Args...)对最终用户有意义,解决此问题的一种方法是使用部分特化:

template<class F, class Alloc> class C; //undefined
template<class T, class... Args, class Alloc>
class C<T(Args...), Alloc> {
    // implementation
};

根据实际需求,类型擦除分配器也可能值得考虑。