【问题标题】:Interesting behavior of compiler with namespaces带有命名空间的编译器的有趣行为
【发布时间】:2014-11-16 13:09:30
【问题描述】:

假设以下代码:

#include <iostream>
using namespace std;

namespace X
{
  class A{};

  void f(A a){}

  void g(int a){}
}

int main()
{
  X::A a;
  f(a);
  g(5);
}

当我编译代码时,出现以下编译错误:

main.cpp:在函数'int main()'中:
main.cpp: 错误: 'g' 未在此范围内声明

所以函数f 被完美编译,但g 不是。如何?它们都属于同一个命名空间。编译器是否从 X::A 类型的参数中推断出函数 f 属于 X 命名空间?在这种情况下编译器的行为如何?

【问题讨论】:

    标签: c++ compiler-errors namespaces argument-dependent-lookup


    【解决方案1】:
    X::A a;
    f(a);
    

    因为 Argument-Dependent Lookup(也称为 Koenig Lookup)而起作用。 a 是命名空间X 内的A 类的对象,当编译器搜索可匹配函数f 时,它会在这种情况下查找命名空间X。请参阅Argument Dependent Lookup 了解更多信息。

    【讨论】:

    • 所以这就是为什么当我认为我需要前缀时看到 std:: 被拉进来的原因。整洁。
    【解决方案2】:

    这适用于函数调用表达式:

    f(a);
    

    因为X::A所属的命名空间包含在f函数的查找中,由于argument dependent lookup(ADL),cppreference解释ADL如下:

    依赖于参数的查找,也称为 ADL 或 Koenig 查找,是 用于在 函数调用表达式,包括隐式函数调用 重载运算符。这些函数名称在 除了范围和命名空间之外,它们的参数的命名空间 由通常的非限定名称查找考虑。

    依赖于参数的查找使使用定义的运算符成为可能 在不同的命名空间中

    这在draft C++ standard 部分3.4.2 中进行了介绍依赖于参数的名称查找

    函数调用 (5.2.2) 中的后缀表达式是非限定 ID 时,不考虑其他命名空间 在通常的非限定查找 (3.4.1) 期间,可能会搜索到,并且在这些命名空间中,命名空间范围 可能会发现不可见的友元函数或函数模板声明 (11.3)

    接着说:

    对于函数调用中的每个参数类型 T,都有一组零个或多个关联命名空间和一个 要考虑的零个或多个相关类的集合。确定命名空间和类的集合 完全取决于函数参数的类型(以及任何模板模板参数的命名空间)。

    并包括以下项目符号:

    如果 T 是类类型(包括联合),则其关联的类是:类本身;它所属的类 成员,如果有的话;及其直接和间接基类。 其关联的命名空间是命名空间 其关联类是其中的成员。[...]

    下面提供了一个类似的例子来解决你的问题:

    namespace NS {
      class T { };
      void f(T);
      void g(T, int);
    }
    
    NS::T parm;
    void g(NS::T, float);
    
    int main() {
      f(parm); // OK: calls NS::f
      extern void g(NS::T, float);
      g(parm, 1); // OK: calls g(NS::T, float)
    }
    

    函数调用表达式:

    g(5);
    

    不起作用,因为 ADL 不会为基本类型的参数添加任何命名空间。

    Herb Sutter 在 Gotw #30What's In a Class? - The Interface Principle 中介绍 ADL。

    【讨论】:

    • 示例中的extern 有什么影响吗?
    • @dyp ugh,谢谢你指出这一点,我希望每个人在阅读其他人的答案时都一样细心。
    • 我一直对阅读您的答案很感兴趣,因为它们通常非常全面,而且您使用的资源范围很广。乍一看,我只是在理解第一句话时遇到了一些麻烦(你知道,介绍通常是一个简单的摘要/概述)。
    • @dyp C++ 是我的工作,也是我喜欢做的事情,因此编写全面的答案让我保持诚实和学习。我并不总是做得很好,但我很乐意根据建设性的反馈做出改进。
    • @0x499602D2 所以extern 不会影响结果,g 隐含地是externg 的本地声明确实很重要。我昨天一定很慢,今天早上我想通了为什么它很快就起作用了。
    【解决方案3】:

    当代码f(a)时,由于ADL(参数依赖查找,也称为Koenig查找),编译器在namespace X中找到函数void f(A a){}

    A 在命名空间X 中声明,因此当编译器需要查找f 的定义时,它包括来自该命名空间的可能性,因为A 类型的对象a 在其中命名空间(声明为X::A a;)。

    另一方面,int 未在 namespace X 中声明,因此 namespace X 不包含在查找中。由于没有找到f的对应函数,所以编译失败。

    【讨论】:

    • 参数依赖查找
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-13
    • 2019-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多