【问题标题】:Why can't I use foo::bar functions within sub::bar when using namespace foo?为什么在使用命名空间 foo 时不能在 sub::bar 中使用 foo::bar 函数?
【发布时间】:2019-07-09 12:16:46
【问题描述】:

考虑以下程序:

namespace foo {
namespace sub {
int f();
} // namespace sub 
} // namespace foo 

namespace bar {
namespace sub {

int g()  {
    using namespace foo;
    return sub::f() + 1;
}

} // namespace sub 
} // namespace bar 

我希望它可以编译,但是 - 它没有:

$ g++-6 -c a.cpp
a.cpp: In function ‘int bar::sub::g()’:
a.cpp:12:9: error: ‘f’ is not a member of ‘bar::sub’
  return sub::f() + 1;
         ^~~
a.cpp:12:9: note: suggested alternative:
a.cpp:3:5: note:   ‘foo::sub::f’
 int f();
     ^

clang 也没有运气:

$ clang++-6.0 -c a.cpp
a.cpp:12:9: error: no member named 'f' in namespace 'bar::sub'; did you mean 'foo::sub::f'?
        return sub::f() + 1; 
               ^~~~~~
               foo::sub::f
a.cpp:3:5: note: 'foo::sub::f' declared here
int f();
    ^
1 error generated.

它也知道我想要哪个功能!

现在,我知道如果我在这里同时拥有 foo::sub::fbar::sub::f,事情可能会变得模棱两可。但是 - 为什么 bar::sub “隐藏” foo::sub 即使我明确要求使用 foo::sub 函数?

要清楚答案可能是什么:

  • 解释为什么这是(最)合理的行为,而我的期望是不合理的。
  • 在 C++ 标准中采用这种方式的基本原理。
  • 引用标准的一句话说这需要是这样的;也许上下文会帮助我理解为什么会这样。

动机:我已经编写了我的代码,以便函数之前的最后一个命名空间是方法名称的一部分,从某种意义上说,如果不预先添加命名空间,您将无法理解函数的作用。因此,我不想像在C 中那样说sub_f(),而是希望能够说sub::f()。事实证明,如果不预先添加更长的命名空间路径,这样做是很困难的。

【问题讨论】:

  • 两个相似的sub 令人困惑,你可以在那里有相似的功能。我认为编译器告诉你要明确是正确的(尽管using namespace foo)。
  • 你并没有真正明确地要求foo::sub,你只是要求找到恰好在foo中的sub::f
  • @MatthieuBrucher: 1. 我很明确 - 我说过我想使用 foo::sub 函数。如果不进入单一功能级别,我不能比这更明确。 2. 如果函数名称不冲突,就不会造成混淆——至少在我明确表示的时候。
  • 您没有明确要求使用foo::sub,您只是导入了namespace foo。那是非常不同的。如果您想明确,请使用foo::sub::f。这是明确的。

标签: c++ scope namespaces language-lawyer


【解决方案1】:

我认为阻止您尝试做的标准的相关部分是:

6.3.10 名称隐藏[basic.scope.hiding]
...
4 在查找由命名空间名称限定的名称期间,原本可以通过 using 指令可见的声明可以被包含使用指令;

subfoobar 命名空间中都有。尽管有using 指令,bar 中的sub 却隐藏了foo 中的sub。这就是为什么foo::sub 中的f() 也不可见的原因。

【讨论】:

  • 可以是”?标准的措辞不是很奇怪吗?另外 - 当我们谈论命名空间时,为什么“隐藏”不只限于命名空间中的实际内容?
  • @einpoklum:是的。它出现在该部分的多个位置。
  • @P.W:如果它“可以是”,那是不是意味着它“不一定是”?
  • @einpoklum,'如果它“可以”,那不就意味着它“不一定必须”吗?'不,不一定。 "Essentially, can denotes capability or capacity, and may denotes permission or opportunity. 这里使用的是“有可能”而不是“有可能”的意思。
  • 隐藏仅限于命名空间中的内容,这就是为什么这部分标准在这种情况下不相关。此规则仅适用于存在 bar::sub::sub 命名空间的情况。在那种情况下,bar::sub::sub 将隐藏foo::sub
【解决方案2】:

[namespace.udir] 说(强调我的):

-2- using-directive 指定指定命名空间中的名称可以在 using-directive 出现在 之后的范围内使用使用指令在非限定名称查找 (6.4.1) 期间,名称出现 就好像它们是在最近的封闭命名空间中声明的一样,该命名空间同时包含 using-directive 和指定的命名空间。 [注意: 在这种情况下,“包含”意思是“直接或间接包含”。 ——尾注]

在您的示例中,“指定命名空间中的名称”只是 foo::sub,因为这是在命名空间 foo 中声明的唯一名称,以及“包含 using-directive 的最近封闭命名空间 和指定的命名空间”是全局命名空间。所以foo::sub 出现(用于名称查找)就好像它在全局命名空间中一样,如下所示:

namespace sub {
int f();
} // namespace sub 

namespace bar {
namespace sub {

int g()  {
    return sub::f() + 1;
}

} // namespace sub 
} // namespace bar 

现在应该更明显的是sub::f 不会找到任何东西。 sub::f 的名称查找从查找 sub 开始。它没有在封闭的命名空间(bar::sub)中找到它,所以它在下一个最里面的封闭范围(bar)中查找并在那里找到sub。此时sub 的名称查找停止,它不会在全局命名空间中查找foo::sub,它会在using-directive 中找到可见的foo::sub

名称查找的下一步尝试在为sub 找到的范围内查找f,但在它找到的命名空间中没有f,因此名称查找失败。

Using-directives 不像为命名空间的每个成员添加 using-declaration 那样工作,它们比 using-declarations “弱”。这样做的原因是,真正在命名空间中声明的名称优先于简单地通过 using 指令可见的名称。 rodrigo's answer 对一个相关问题进行了更详细的解释。

【讨论】:

  • 1.这部分标准取消了我声明的意图,即专注于搜索 foo 中的内容而不是 bar 中的内容。这很烦人。如果 2. 我仍然希望 sub::f() 在您编写的“as-if”代码 sn-p 中工作,它将得到缓解。如果愿意,可以称之为“命名空间匹配失败不是错误”。
  • 2.但这不是名称查找的工作方式,它总是在第一次匹配时停止 (eel.is/c++draft/basic.lookup.unqual#1)。如果你有一个类 ::foo::bar 并且它的一个成员调用 bar::oops() 而不是 bar::ops() (即一个简单的错字)你不希望编译器考虑 ::bar::oops() 只是因为在其他地方还有另一个 bar 范围程序。
  • 让我稍微修改一下这个例子,因为你把我逼到了一个标准没有涵盖的角落......假设bar类不在::foo中,而是在baz::foo。现在,在您的示例中,如果某个方法显示using namespace baz; ::bar::oops(); - 那么是的,我肯定希望编译器使用::baz::bar::oops()using namespace 是我向编译器提示我感兴趣的内容。
  • 显然,如果您说::bar::oops(),那么您指的不是::baz::foo::bar 类,所以我看不出这有什么关系。如果您使用以:: 开头的完全限定名称,则名称查找从:: 开始,而不是最内层范围,这就是我所说的情况。这个讨论似乎毫无意义,名称查找在第一次匹配时停止,有充分的理由。
【解决方案3】:

回答您的第一点:
将 a 添加到程序时会发生什么:

namespace sub
{
    int f();
}

现在编译器应该选择什么: ::sub::f() 或(使用 ::foo::)sub::f() ?

【讨论】:

  • 无论哪个。或者它会给我一些错误或警告。如果我引起了歧义,那么“我”要承担后果。
猜你喜欢
  • 2021-04-24
  • 2020-10-03
  • 1970-01-01
  • 1970-01-01
  • 2017-06-23
  • 2022-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多