【问题标题】:where's the relevant rule for phase two name lookup of template instantiation in the current standard当前标准中模板实例化的第二阶段名称查找的相关规则在哪里
【发布时间】: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 footemp.dep#2 的依赖名称,因此,该名称的查找名称规则应遵循以下规则,这在 [temp.dep#2] 的注释中有所提及

[ 注意:此类名称是未绑定的,并且在模板定义的上下文和实例化点的上下文([temp.dep.候选人])。 ——尾注]

依赖名的查找规则在c++17标准中明确写成,即:
temp.dep.res#1

在解析从属名称时,会考虑来自以下来源的名称:

  • 在模板定义处可见的声明。
  • 来自与来自实例化上下文 ([temp.point]) 和定义上下文的函数参数类型相关联的命名空间的声明

还有temp.dep.candidate#1

对于后缀表达式是从属名称的函数调用,使用通常的查找规则([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


【解决方案1】:

换句话说,这些规则意味着只有 ADL 用于执行第二阶段的查找。

从给定的规范引用中得出的结论是错误的。您似乎混淆了两个不同的概念。

第一个概念是通常所说的“两阶段查找”:当模板被解析时,依赖于模板参数的名称在解析时无法被查找。它们只能在实例化时查找。查找有两个阶段:一个用于非依赖名称,一个用于依赖名称。

第二个概念只是名称查找,它有两个名称范围进行搜索:常规非限定查找(在 [basic.lookup.unqual] 中定义)和参数相关查找(在 [basic.lookup.argdep] 中定义) .非限定查找在代码本身的范围内进行搜索,而 ADL 在函数参数的命名空间范围内进行搜索。

模板名称查找的两个阶段都使用两个这些范围。需要明确的是,从属名称查找会进行非限定查找。这就是为什么您引用的要点之一说“对于使用不合格名称查找的查找部分......”。如果从属名称查找不使用非限定名称查找规则,则不会提及它们。

现在,从属名称查找确实限制了将候选名称与常规规则进行比较的内容。但它并不局限于只是 ADL;它执行两个范围的查找。

正如您所指出的,这些限制曾经在 [temp.dep.candidate]/1 中指定。但他们移动了一点。

对于不合格的查找部分,新的 [temp.dep.candidate]/1 涵盖了您引用它的内容,因为它说“通常的查找规则来自模板定义上下文”。这将非限定查找的上下文指定为仅模板的定义。

请注意,第二个要点也表示要查看该范围,因此它涵盖了其中的一部分。

第二个要点的其余部分由[basic.lookup.argdep]/4.5 覆盖:

如果查找是针对从属名称([temp.dep],[temp.dep.candidate]),则如果 D 对限定名称查找([namespace.qual])可见,则 N 中的任何声明 D 都是可见的在查找的实例化上下文([module.context])中的任何点,除非 D 在另一个翻译单元中声明,附加到全局模块,并且被丢弃([module.global.frag])或具有内部链接.

这扩展了查找以包括“实例化上下文”,它在很大程度上匹配 C++17 文本(尽管有基于模块的修改)。

【讨论】:

  • 您的意思是通常的名称查找将在实例化时间再次从实例化上下文中执行吗?这也将经历通常的非限定名称查找,然后是 ADL,只是规则限制这些从 instatiation 上下文通过通常的非限定名称查找找到的候选者不要添加到候选集(即,只考虑这些候选人是由 ADL 找到的),对吗?
  • @jackX: "仅考虑 ADL 找到的这些候选者" 不。在依赖名称查找期间仍会发生不合格名称查找,但它不会根据“实例化”找到名称语境”。它仍然在第二阶段寻找它们,但它只寻找可从“模板定义上下文”访问的名称。
  • 我试图改写我的理解。实例化模板时,将对依赖名称执行名称查找,不同之处在于通过非限定名称查找找到的这些声明是从定义上下文中找到的,而ADL找到的这些声明是从两者中找到的定义上下文实例化上下文。这也意味着这些可以通过实例化上下文中的非限定名称查找找到的声明不是候选的。对吗?
  • @jackX:你在这里说的是正确的。但这并没有真正改变你的问题,因为它在 C++17 和 C++20 中都是一样的。正如我在回答中指出的那样,他们只是稍微移动了一下。
  • 您指出 [temp.dep.candidate#1] 中的原始第二个项目符号已移至 [basic.lookup.argdep]/4.5。还有一个问题,在我看来,通常的查找规则这个措辞是非规范性的,它是否包括“ADL”?
猜你喜欢
  • 2021-09-25
  • 1970-01-01
  • 2015-01-26
  • 2020-12-10
  • 1970-01-01
  • 1970-01-01
  • 2010-10-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多