当某些依赖函数在使用之后和使用之前的范围内时,有谁知道编译器和/或语言标准发生了什么?我使用的是 Stroustrup 在 C++ 第 4 版第 747 页中的示例的精密拷贝。在 DEP_NAME 示例中 g
和Q
可以在模板函数 f
之后声明和 都在范围内,但是在非 DEP_NAME 示例中,情况恰恰相反。
感谢您的指导!
#define DEP_NAME
#ifdef DEP_NAME
template<typename T> T f(T a)
{
return g(a); // OK: a is a dependent name and therefore so is g
}
// can be declared after f
class Q { };
Q g(Q e)
{
return e;
}
#else
// must be declared before f
class Q { };
Q g(Q e)
{
return e;
}
template<typename T> T f(T a)
{
return g(Q{});
}
#endif
int main(int argc, char *argv[])
{
Q z = f(Q{});
return 0;
}
最佳答案
以下所有标准引用均指 N4659: March 2017 post-Kona working draft/C++17 DIS .
延迟查找依赖名称允许 ADL 查找在函数模板定义之后声明的名称
函数模板定义中的所有非依赖名称都必须在定义点声明。对于给定的专门化,仅需要在第一次实例化时声明从属名称,如果可以通过特定实例化的参数相关查找 (ADL) 找到它们。在您的第一个示例中, g
是一个依赖名称,因为它取决于类型模板参数,而且可以通过 ::Q
上的 ADL 找到它,因此第一个示例结构良好。在第二个示例中, g
不是依赖名称,因为它不依赖于类型模板参数,并且由于该名称在函数模板定义点不可见,因此第二个示例是错误的-形成。
我们可以将相关标准段落中引用的规则(参见第详细信息部分)总结为:
- 对非依赖名称的查找遵循通常的查找规则,因此需要在函数模板定义时声明该名称,
- 对从属名称的查找被推迟到给定的实例化为止。但是,在此实例化时,只有参数相关查找 (ADL) 才能找到在函数模板定义时未声明的名称,但在给定实例化时声明。
后一个项目符号的规则意味着以下示例格式良好:
namespace ns {
template<typename T> T f(T a) {
// Can find ::ns::g(Q) only via ADL on T for
// an instantiation of f with T == ::ns::Q.
return g(a);
}
class Q {};
Q g(Q e) { return e; }
} // namespace ns
int main() {
(void) f(ns::Q{});
return 0;
}
如下例:
template<typename T> T f(T a) {
// Can find ::ns::g(Q) only via ADL on T for
// an instantiation of f with T == ::ns::Q.
return g(a);
}
namespace ns {
class Q {};
Q g(Q e) { return e; }
} // namespace ns
int main() {
(void) f(::ns::Q{});
return 0;
}
而以下示例格式不正确,因为 ADL 找不到 g
来实例化 ::ns::Q
:
template<typename T> T f(T a) {
// Cannot find ::g(Q) as ADL on T for
// an instantiation of f with T == ::ns::Q
// will only consider the ::ns namespace.
return g(a);
}
namespace ns {
class Q {};
} // namespace ns
::ns::Q g(::ns::Q e) { return e; }
int main() {
(void) f(::ns::Q{});
return 0;
}
对于后者,编译器(在本例中为 Clang)甚至会显示一条指导性错误消息,说明程序格式错误的原因:
error: call to function 'g' that is neither visible in the template definition nor found by argument-dependent lookup
详细信息
[temp.res]/9状态[摘录,强调我的]:
[temp.res]/9 When looking for the declaration of a name used in a template definition, the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) are used for non-dependent names. The lookup of names dependent on the template parameters is postponed until the actual template argument is known ([temp.dep]). [ Example: ... ] [...]
[temp.dep.res]/1很明显,只有在模板定义时可见的声明才会被考虑用于非限定(依赖)名称查找[强调我的]:
[temp.dep.res]/1 In resolving dependent names, names from the following sources are considered:
- (1.1) Declarations that are visible at the point of definition of the template.
- (1.2) Declarations from namespaces associated with the types of the function arguments both from the instantiation context ([temp.point]) and from the definition context.
[temp.dep.candidate]/1 中重复的事实[强调我的]:
[temp.dep.candidate]/1 For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) except that:
- (1.1) For the part of the lookup using unqualified name lookup, only function declarations from the template definition context are found.
- (1.2) For the part of the lookup using associated namespaces ([basic.lookup.argdep]), only function declarations found in either the template definition context or the template instantiation context are found.
关于c++ - 依赖名称和范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62984097/