何时提供默认参数作为模板参数
When to supply default arguments as template arguments
template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T>){ //#1
}
int main(){
func(Test<int>{}); //#2
}
标准中似乎没有规则提到模板参数需要默认参数的情况。
In dcl.fct.default#1
如果在参数声明中指定了初始值设定项子句,则此初始值设定项子句将用作默认参数。默认参数将在缺少尾随参数的调用中使用。
在本节中,规则显式描述何时为函数调用提供默认参数。但是,我还没有在标准中找到与上述句子类似的引用,该引用描述了何时提供默认参数作为模板参数。
比如Test<T>
#1
.也许在#1
,专业化会Test<T,T>
,只是推断。但是,标准中没有正式的术语明确描述这一点。
唯一暗示默认参数作为模板参数的引号是以下规则:
temp#arg-8
当简单模板 id 不命名函数时,当需要默认参数的值时,将隐式实例化默认模板参数。[ 示例:
templatestruct S { };
S* p; p 的类型是 S<bool,>*
U 的默认参数被实例化以形成类型S<bool,>*。 — 结束示例 ]
考虑#1
处的代码,默认参数是否需要在#1
?如果是(似乎在这一点上需要,因为如果我不为模板参数 U 指定默认参数,那么在这一点上#1
会发生错误。 参见 Godbolt 结果),根据上面的引用,默认的模板参数需要隐式实例化,但是在这一点上,T
是一个模板参数,并且这种函数模板的定义什么都没有实例化(它只是一个函数模板定义点)。那么,这句话是如何解释这一点的呢?
[temp.names]里面也有这样一段:
模板 ID 有效,如果
- 每个没有默认模板参数的不可推导非包参数都有一个参数,
因此,可以认为需要默认参数意味着需要默认参数才能使模板 id 有效,并且当缺少模板参数时,该参数是默认参数值。但是我在类模板的标准中没有找到任何明确的内容。对于函数模板,这更明确。可能没有人指出标准中的这个漏洞,因为这是一种常见的模式:默认用于代替用户未提供的内容。也许它不会改变,因为英语词典中默认的定义已经给出:
某事自动发生或出现的方式,尤其是在计算机上,如果您不做出任何不同的选择 -- 坎布里奇在线词典
根据上面的引用,默认的模板参数需要隐式实例化,但是在这一点上,
T
是一个模板参数,并且这种函数模板的定义什么都没有实例化(此时它只是一个函数模板定义)。那么,这句话是如何解释这一点的呢?
关键是实例化需求和引用时间之间的区别。在 [temp.arg]/8 的示例中,U
的默认模板参数被实例化,因为它是实例化S<bool>
所必需的,这明确地解析为S<bool, int>
。在 OP 自己的示例中,只有在重载解析解析为最佳可行重载并因此实例化它之后,才会实例化依赖于U
的实例化默认模板参数void func(Test<T>)
。但是,仅检查候选重载作为重载解析的一部分,不会导致实例化任何候选项或任何函数模板候选参数(这些参数本身就是模板)。但是,它将引用所述模板参数作为重载解决过程的一部分(丢弃不可行的候选参数)。
从[温度扣除]/1[强调我的]:
引用函数模板专用化时,所有模板参数都应具有值。这些值可以显式指定,或者在某些情况下,可以从使用中推断出来,或者从默认模板参数中获取。[...]
对于 OP 的特定示例,前者(实例化)仅在重载解析完成并且实例化了最佳可行重载(进而实例化类模板函数参数)之后发生。然而,后者(引用的函数/类模板专用化)也适用于重载解析期间,即使丢弃不可行或不是最佳可行匹配的候选项,这些候选项永远不会导致实例化。
从[温度扣除]/2 [强调我的]:
指定显式模板参数列表时,模板参数必须与模板参数列表兼容,并且必须生成有效的函数类型,如下所述;否则类型推断失败。具体而言,在计算与给定函数模板相关的显式指定的模板参数列表时,将执行以下步骤:
- (2.1)指定的模板参数必须与实物模板参数匹配(即类型、非类型、模板)。参数不能多于参数,除非 [...]
从[温度扣除]/6[强调我的]:
在模板参数推导过程中的某些点,有必要采用使用模板参数的函数类型,并将这些模板参数替换为相应的模板参数。当任何显式指定的模板参数被替换到函数类型中时,在模板参数推导开始时完成此操作,当从默认参数推导或获取的任何模板参数被替换时,在模板参数推导结束时再次执行此操作。
很明显,作为"模板参数推导结束">的一部分,任何未明确指定的模板参数都将从默认参数中推导或获取,并在丢弃或将其排序为可行重载之前替换到候选函数中。这适用于与实例化没有任何关系,并且仅在作为重载解析的一部分引用给定专用化(可能尚未或永远不会实例化)的上下文中适用。
最后,从 [temp.deduct.type]/3 [强调我的]:
给定的类型
P
可以由许多其他类型、模板和非类型值组成:
- [...]
(- 3.3)类模板的专用化类型(例如,
A<int>
)包括专用化的模板参数列表引用的类型,模板和非类型值。
我们注意到,作为函数模板参数的函数模板参数推导的一部分,函数模板参数类型(用于对参数的推导)包括模板参数的模板参数列表所引用的类型,这意味着引用了特定的类模板专用化(函数模板参数),以便根据 [temp.deduct]/1,它将对未显式指定的任何模板参数进行模板参数推断和默认参数检查。
- 使函数参数默认为周围范围
- 如何将值传递给其参数(默认参数)
- 当“std::make_index_sequence”和“std::index_sequence”用于模板参数默认类型时
- C ,构造器中允许的参考参数默认值
- 哪些规则控制参数默认赋值?
- 错误:上一个规范后给出的参数默认参数
- 是否可以设计一个包含模板参数默认值的类
- 构造函数中参数C++默认值
- 如何设置依赖于其他参数的参数默认值
- C++模板参数默认函数实现
- QInput对话框参数默认值
- g++4.8.2上列表方法参数默认初始化时出错
- 在函数定义中指定参数默认值会导致错误 C2143:语法错误:'='之前缺少')'
- 函数模板:将第一个模板参数默认为第二个
- 模板模板参数默认可以引用其他模板类型的参数
- 参数默认为先例参数的值
- 如何修改一个类,使它只有一个成员函数,所有参数默认
- 内置类型的模板参数默认值
- 是否可以使用构造函数或对象作为其他类方法的参数默认值?
- 模板参数默认为更高版本