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

2021-12-13 00:00:00 templates c++ c++11 variadic-templates

如果参数列表绑定到一个类,我不明白为什么参数包必须位于参数列表的末尾,而如果参数列表是成员方法声明的一部分,则约束会放宽.

换句话说,这个编译:

class C {template无效 fn() { }};

以下没有:

templateC类{};

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

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

templateC类{};

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

<块引用>

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

所以,我只是想完全理解为什么它在某些情况下被接受,而在另一些情况下却不是.

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

解决方案

它对函数模板有效,但仅当参数推导可以帮助编译器解析模板参数时,就目前而言,您的函数模板示例几乎没有用,因为

>

template无效 fn() { }int main() { fn();}

test.cpp:在函数int main()"中:test.cpp:2:32: 错误:没有匹配的函数来调用fn()"int main() { fn();}^test.cpp:1:57:注意:候选:模板无效 fn()template无效 fn() { }^test.cpp:1:57: 注意:模板参数推导/替换失败:test.cpp:2:32: 注意:无法推导出模板参数S"int main() { fn();}

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

更有用的函数模板应该是这样的

templatevoid fn(S s) { }

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

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

来自 n4567 草案

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

[temp.param]/11

<块引用>

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

(如果它们被推导出来,就会像函数模板示例中那样含糊不清).

I don't get the reason for which a parameter pack must be at the end of the parameter list if the latter is bound to a class, while the constraint is relaxed if the parameter list is part of a member method declaration.

In other terms, this one compiles:

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

The following one does not:

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

Why is the first case considered right and the second one is not?
I mean, if it's legal syntax, shouldn't it be in both the cases?

To be clear, the real problem is that I was defining a class similar to the following one:

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

Having the allocator type as the last type would be appreciated, but I can work around it somehow (anyway, if you have a suggestion it's appreciated, maybe yours are far more elegant than mine!!).
That said, I got the error:

parameter pack 'Args' must be at the end of the template parameter list

So, I was just curious to fully understand why it's accepted in some cases, but it is not in some others.

Here is a similar question, but it simply explains how to solve the problem and that was quite clear to me.

解决方案

It is valid for function templates but only when argument deduction can help the compiler resolve the template parameters, as it stands your function template example is virtually useless because

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>(); }

the compiler has no way of determining which template parameters belong to the parameter pack, and which to S. In fact as @T.C. points out it should actually be a syntax error because a function template defined in this manner cannot ever be instantiated.

A more useful function template would be something like

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

as now the compiler is able to unambiguously match the function parameter s with the template type S, with the side effect that S will always be deduced - all explicit template parameters after the first will belong to Args.

None of this works for (primary) class templates, parameters aren't deduced and it's expressly forbidden:

From draft n4567

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

[temp.param] / 11

[...]If a template-parameter of a primary class template or alias template is a template parameter pack, it shall be the last template-parameter.[...]

(if they were deduced it would be ambiguous as in the function template example).

相关文章