c++ - 依赖名称和范围

标签 c++ c++11

当某些依赖函数在使用之后和使用之前的范围内时,有谁知道编译器和/或语言标准发生了什么?我使用的是 Stroustrup 在 C++ 第 4 版第 747 页中的示例的精密拷贝。在 DEP_NAME 示例中 gQ可以在模板函数 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/

相关文章:

c++ - 生成错误 Makefile.win FLTK

C++为什么有类头却有 "Unknown Type"?

成员参数的c++运行时实例化

c++ - C++ 中的转换运算符

c++ - 如何限制从主函数访问类函数?

c++ - Visual C++ - 覆盖从 DLL 导入的函数?

c++ - 类型别名中的详细类型说明符

visual-studio - constexpr 无法使用 Visual C++ 编译器 2013 年 11 月 CTP (CTP_Nov2013)

c++ - std::unordered_multiset 中的桶

c++ - 如何在 Raylib 中用像素追踪路径?