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

sky*_*ack 26 c++ templates variadic-templates c++11

如果参数列表必须位于参数列表的末尾,如果后者绑定到类,则不会得到参数列表的原因,如果参数列表是成员方法声明的一部分,则放宽约束.

换句话说,这个编译:

class C {
    template<typename T, typename... Args, typename S>
    void fn() { }
};
Run Code Online (Sandbox Code Playgroud)

以下不是:

template<typename T, typename... Args, typename S>
class C { };
Run Code Online (Sandbox Code Playgroud)

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

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

template<typename T, typename... Args, typename Allocator>
class C { };
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

use*_*267 16

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

template<typename T, typename... Args, typename S> void fn() { }
int main() { fn<int, int, int>(); }
Run Code Online (Sandbox Code Playgroud)
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>(); }
Run Code Online (Sandbox Code Playgroud)

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

一个更有用的函数模板就是这样的

template<typename T, typename... Args, typename S> void fn(S s) { }
Run Code Online (Sandbox Code Playgroud)

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

这些都不适用于(主)类模板,不推导出参数,并且明确禁止参数:

来自草案n4567

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

[temp.param]/11

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

(如果推断它们会像函数模板示例中那样模糊不清).

  • @skypjack如果函数类型对最终用户有意义,可能是`template <class F,class Alloc> struct meow; template <class Ret,class ... Args,class Alloc> struct meow <Ret(Args ...),Alloc> {};`? (2认同)

T.C*_*.C. 11

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

函数模板的模板参数包不能跟随另一个模板参数,除非该模板参数可以从函数模板的参数类型列表中推导出来或者具有默认参数(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
};
Run Code Online (Sandbox Code Playgroud)

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

  • 编译器是GCC5,如果这个问题不存在,我应该在他们的bug跟踪系统上打开一张票吗? (2认同)