函数模板参数包后跟模板参数和特化

Posted

技术标签:

【中文标题】函数模板参数包后跟模板参数和特化【英文标题】:Function template parameter pack followed by template parameter and specialisation 【发布时间】:2019-06-29 17:06:12 【问题描述】:

在定义函数模板中使用的模板参数包是否可以跟在另一个模板参数之后,当该参数仅在定义中给出其所需的默认值时;而不是宣言?考虑以下示例:

template <typename ...Ts, typename T>
auto sz(Ts...);

template <typename ...Ts, typename T = int>
auto sz(Ts...)  return sizeof...(Ts); 

我发现 GCC 和 Clang 在这一点上存在分歧(GCC 给出了编译错误)。

【问题讨论】:

根据this code,我已经测试了与您类似的其他三个案例,您的示例是当前 Clang 和 GCC 版本不同的唯一案例。正如下面讨论的答案,我认为 Clang 允许这样做是错误的。不过,我也很想知道:你为什么想要这种特殊的、异国情调的安排? 嗨@sigma。这是其他人粗略代码中 SFINAE 选择系统的一部分。我删除了 SFINAE 以制作您在上面看到的 MWE。 【参考方案1】:

-- EDIT -- 在最初的误解后更正。

我猜g++是对的,clang++是错的。

根据 C++17 标准,17.1.11,

模板参数 一个函数模板的包后面不应有另一个模板参数,除非该模板 参数可以从函数模板的参数类型列表(11.3.5)中推导出来或具有默认值 论点 (17.8.2)。

因此,在可变参数包之后应该接受具有默认值的模板参数。

问题是:我们只能在函数声明或函数定义中设置模板默认值(与函数声明不同)?

事实上,你可以简单地用

来简化你的问题
template <typename>
void foo ();

template <typename = int>
void foo ()
  

int main ()
 
   foo();
 

被clang++接受,被g++拒绝。

这对我来说并不完全清楚,但是在 17.1.9 中我读到了

默认值 模板参数 可以在模板声明中指定。 默认 模板参数 不得在 模板参数列表 s 的定义 出现在成员类之外的类模板的成员。默认 模板参数 将 不在友元类模板声明中指定。如果友元函数模板声明指定了一个 默认 模板参数 ,该声明应是一个定义,并且应是该声明的唯一声明 翻译单元中的函数模板

该片段明确指出“可以在模板声明中指定默认模板参数”。不在“定义”中。

排除“在成员的类之外出现的类模板的成员的定义的template-parameter-list中不应指定默认的模板参数”,所以我最初的结论是模板泛型函数可以在“定义”中指定默认模板参数,但这部分是关于“类模板”,所以模板参数是关于类的,而不是关于成员的。

正如 sigma 在 17.1.10 中指出的(谢谢!),我们可以阅读

默认设置 模板参数 可以使用的 s 是通过合并来自的默认参数获得的 模板的所有先前声明都以相同的方式默认函数参数是

再次:“声明”,没有“定义”。

所以我认为 clang++ 是错误的,而 g++ 是正确的。

【讨论】:

有趣。我认为代码应该仅在在初始声明中指定T 的默认值时编译,因为 17.1.11 和另外 17.1.14:“可用的默认模板参数集通过以与默认函数参数相同的方式合并模板的所有 prior 声明中的默认参数来获得使用”(强调我的)。 @sigma - 回顾一下......也许你是对的。我想我误解了关于“类模板成员的定义”的部分:它是类模板,而不是类的成员模板。答案已更正(我希望)。谢谢。 感谢@sigma 和@max66。有很多值得考虑的地方。看起来 GCC 是正确的..我仍然有一个问题是 17.1.14 引用是否意味着添加第二个声明 sz,其中 T 被赋予默认值,并删除 T从定义的默认值,应该导致有效的代码? (这里 Clang 很高兴,但 GCC 不高兴。) @user2023370 - 抱歉,我误解了:给定template &lt;typename&gt; void foo (); template &lt;typename = int&gt; void foo (); template &lt;typename&gt; void foo () 和调用foo();,我的clang++ 编译和g++ 给出了一个错误......嗯......在我看来,在这种情况下是正确的 clang++ 但我不确定:17.1.14 的示例是关于多个类声明,非函数类声明。 @user2023370:该部分没有具体说明模板的类型,所以我认为它同样适用于函数模板。我做了一些重新阅读并且更相关的是第 15 节,说参数只能跟随参数包,如果它们有默认值或者可以从参数推导出来,以及 16,“不应给出模板参数同一范围内的两个不同声明的默认参数。”我将其视为完成答案:不,在您的示例中,只有第一个声明可以(并且必须)为T 提供默认值。

以上是关于函数模板参数包后跟模板参数和特化的主要内容,如果未能解决你的问题,请参考以下文章

C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译

没有参数的函数的模板特化

C++模板进阶操作 —— 非类型模板参数模板的特化以及模板的分离编译

C++ 模板(进阶)

C++之模板进阶

C++之模板进阶