【发布时间】:2021-05-23 17:37:04
【问题描述】:
template<typename T>
void fun(T t){
foo(t); //#1 foo is a dependent name
}
void foo(int){
}
int main(){
fun(0); // #2 ill-formed
}
由于 unqualified-id foo 是 temp.dep#2 的依赖名称,因此,该名称的查找名称规则应遵循以下规则,这在 [temp.dep#2] 的注释中有所提及
[ 注意:此类名称是未绑定的,并且在模板定义的上下文和实例化点的上下文([temp.dep.候选人])。 ——尾注]
依赖名的查找规则在c++17标准中明确写成,即:
temp.dep.res#1
在解析从属名称时,会考虑来自以下来源的名称:
- 在模板定义处可见的声明。
- 来自与来自实例化上下文 ([temp.point]) 和定义上下文的函数参数类型相关联的命名空间的声明。
对于后缀表达式是从属名称的函数调用,使用通常的查找规则([basic.lookup.unqual]、[basic.lookup.argdep])找到候选函数,除了:
- 对于使用非限定名称查找的部分查找,只能找到来自模板定义上下文的函数声明。
- 对于使用关联命名空间 ([basic.lookup.argdep]) 的查找部分,只能找到在模板定义上下文或模板实例化上下文中找到的函数声明。
换句话说,这些规则意味着只有 ADL 用于执行第二阶段的查找。
但是,我在当前标准(即 c++20)中找不到相关规则。 [temp.dep.res#1] 已从当前标准中删除。 [temp.dep.candidate#1] 已修改为:
temp.dep.candidate#1
对于后缀表达式是从属名称的函数调用,候选函数是使用模板定义上下文中的常用查找规则([basic.lookup.unqual]、[basic.lookup.argdep])找到的。 [注意:对于使用关联命名空间([basic.lookup.argdep])的查找部分,在模板实例化上下文中找到的函数声明通过此查找找到,如[basic.lookup.argdep]中所述。 — end note ] 如果调用格式错误或找到更好的匹配,则在相关命名空间中的查找考虑所有翻译单元中这些命名空间中引入的具有外部链接的所有函数声明,而不仅仅是考虑在模板定义和模板实例化上下文,则程序具有未定义的行为。
这只是作为当前标准中的一个注释。在我看来,当前的标准并没有清楚地描述查找规则如何在函数调用中对不合格的从属名称执行。在目前的标准中,哪里有规定只有ADL才能在第二阶段执行?目前还不清楚。如果将在第二阶段执行通常的不合格查找规则,则 #2 将是格式良好的,因为 void foo(int) 可以通过不合格查找在实例化上下文中找到。如果我遗漏了什么,相关规则在哪里?
【问题讨论】:
-
这个问题我有似曾相识的感觉
-
@LanguageLawyer 该示例出现在关于 SO 的许多问题中。大多数问题都问为什么
fun(0);格式不正确。这个问题被问到查找规则如何在当前标准中对从属名称和相关规则执行。如果我正确理解 NicolBolas 的答案,似乎非限定查找和 ADL 都在实例化上下文中执行。 -
术语“两阶段”比有用更令人困惑——这两个阶段有时被视为对非依赖和依赖名称的查找,但很容易注意到这意味着这些阶段是“定义上下文”和“实例化点”以及规范文本表示它们是“不合格的查找”和(它的子集?)“ADL”。这使得定义上下文中的 ADL 分类不明确,并且对于模块,某些声明只能通过这种查找找到。
-
@DavisHerring 我发现“使用模板定义上下文中的常用查找规则找到候选函数”的措辞已从 P1787 中删除。依赖名称的查找规则替换为temp.res#general-1("如果名称是依赖的(如 [temp.dep] 中指定),则为每个特化(替换后)查找它,因为查找依赖于模板参数。”)。这是否意味着从实例化上下文中通过非限定名称查找找到的这些声明可以作为候选者?
标签: c++ templates language-lawyer c++20