1

我不明白为什么以下代码会出错:

namespace  A
{
    void f(double x){cout<<"A::f(double)\n";}

    void f(string s){cout<<"A::f(string)\n";}

    namespace B
    {
        using namespace A;
        void f(int x){cout<<"B::f\n";}

        void call()
        {
            f(10);  // calls B::f, expected
            f(10.5); // calls B::f, why??
            string s="Hi";
            f(s);  // error, why??
        }
    }
}

我的理解是搜索一个名称,编译器从当前范围开始并不断搜索封闭范围,然后是全局范围,直到找到正确的(正确意味着签名匹配或在方法的情况下可转换)一个,或者如果不存在则给出错误。所以起初我尝试不使用using namespace Ain BB::f然后按预期调用前两个调用,但f(s)出现错误。我认为封闭范围内的名称默认情况下对内部范围可见,但显然我错了。然后我放置using namespace A并认为f(10.5)会要求A::f更好的类型匹配,并且问题f(s)将得到解决,但事实并非如此(我的理解是using关键字将所有内容从正在使用的范围带到当前范围)。有人可以帮助我了解如何在此处应用名称查找,在此先感谢。

注意 我知道如何使它工作。我想知道为什么会这样?C++ 标准用易于理解的语言对此进行了说明。为什么我的理解是错误的?

4

4 回答 4

1

因为,你没有打电话A::f(),这些会发生:

f(10.5); // cast to int
string s="Hi";
f(s);  //I do not know any B::f(), with string argument.

更准确地说,在我粘贴的第一行代码中,编译器会尝试调用f(int a). 你通过10.5并且函数等待一个整数。但是,10.5可以int通过削减其十进制数字将其转换为 ,。此功能是其中之一namespace B

[编辑]

默认值namespace是您在其中拥有函数的那个​​。为了不使用默认值,您必须自己指定它,成为::操作员。

如果你删除f()里面的namespace B,那么编译器会转到namespace A'outer' namespace

也许这个类比scope of the variables可以帮助。

int main() {

  int a = 10;

  {
    int a = 5;
    std::cout << a << std::endl;
  }

  return 0;
}

输出是5。到了 print 的时候a,编译器会去寻找最后声明的a,除非我们另有说明。

于 2014-05-10T14:57:31.840 回答
1

这就是所谓的名称隐藏

可以通过在嵌套声明区域或派生类 (10.2) 中显式声明相同名称来隐藏名称。

的声明隐藏了尽管声明的B::f两个重载。它不起作用的技术原因是不合格查找的工作方式。编译器首先在最本地的命名空间中搜索符号,然后一直向上直到找到它。允许隐式转换,这就是为什么一直被调用以及为什么无法选择重载的原因。要解决此问题,您可以使用声明显式添加外部命名空间函数:A::fusing namespaceB::fstd::stringusing

using A::f;
void f(int) { ... }

void call()
{
    // ...
}

这是一个演示。

于 2014-05-10T15:26:55.857 回答
0

您遇到了一种叫做隐式转换的东西。查看此页面其他页面。

正如在其他答案中指出的那样,编译器仅探索当前范围,并且他尝试将给定的参数转换为预期的参数,如果不能,则失败。

于 2014-05-10T15:03:52.513 回答
0

为了理解编译器行为,参考 C++ 标准中的以下引用很重要

2 using-directive 指定指定命名空间中的名称可以在 using-directive 出现在 using-directive 之后的范围内使用。在非限定名称查找 (3.4.1) 期间,名称看起来好像它们是在最近的封闭命名空间中声明的,其中包含使用指令和指定命名空间。[注:在此上下文中,“包含”是指“直接或间接包含”。——尾注]

在您的示例中,“包含使用指令和指定命名空间的最近封闭命名空间。” 是命名空间 A。那是函数

void f(double x){cout<<"A::f(double)\n";}

void f(string s){cout<<"A::f(string)\n";}

出于非限定查找的目的,它们不是命名空间 B 的成员。

根据 C++ 标准的其他引用

1 名称可以通过在嵌套声明区域或派生类 (10.2) 中显式声明相同名称来隐藏。

这些函数声明被命名空间 B 中 f 的显式声明所隐藏。因此编译器在命名空间 B 中找到了名称 f 并停止了对该名称的进一步搜索。

于 2014-05-10T15:44:56.360 回答