没有歧义的命名空间规范

发布于 2024-07-13 16:57:59 字数 628 浏览 8 评论 0原文

为什么某些语言(如 C++ 和 Python)要求指定对象的命名空间,即使不存在歧义? 我知道这其中存在后门,例如 C++ 中的 using namespace x 或 Python 中的 from x import *。 然而,当只有一个可访问的命名空间包含给定的标识符并且不存在歧义时,我无法理解不希望语言只是“做正确的事情”背后的基本原理。 对我来说,这只是不必要的冗长和违反 DRY 的行为,因为你被迫指定编译器已经知道的东西。

例如:

import foo  # Contains someFunction().

someFunction()  # imported from foo.  No ambiguity.  Works.

VS。

import foo  # Contains someFunction()
import bar  # Contains someFunction() also.

# foo.someFunction or bar.someFunction?  Should be an error only because
# ambiguity exists.
someFunction() 

Why do some languages, like C++ and Python, require the namespace of an object be specified even when no ambiguity exists? I understand that there are backdoors to this, like using namespace x in C++, or from x import * in Python. However, I can't understand the rationale behind not wanting the language to just "do the right thing" when only one accessible namespace contains a given identifier and no ambiguity exists. To me it's just unnecessary verbosity and a violation of DRY, since you're being forced to specify something the compiler already knows.

For example:

import foo  # Contains someFunction().

someFunction()  # imported from foo.  No ambiguity.  Works.

Vs.

import foo  # Contains someFunction()
import bar  # Contains someFunction() also.

# foo.someFunction or bar.someFunction?  Should be an error only because
# ambiguity exists.
someFunction() 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

等数载,海棠开 2024-07-20 16:57:59

原因之一是防止稍后更改代码时(或者对于外部模块/库,当其他人更改代码时)意外引入冲突。 例如,在 Python 中,

from foo import *
from bar import *

如果您知道模块 foobar 没有任何同名的变量,那么您的编写就不会发生冲突。 但是,如果在更高版本中 foobar 都包含名为 rofl 的变量怎么办? 然后 bar.rofl 将在您不知情的情况下覆盖 foo.rofl

我还希望能够查看文件顶部并准确查看正在导入的名称以及它们来自何处(当然,我说的是 Python,但同样的推理也适用于 C++) 。

One reason is to protect against accidentally introducing a conflict when you change the code (or for an external module/library, when someone else changes it) later on. For example, in Python you can write

from foo import *
from bar import *

without conflicts if you know that modules foo and bar don't have any variables with the same names. But what if in later versions both foo and bar include variables named rofl? Then bar.rofl will cover up foo.rofl without you knowing about it.

I also like to be able to look up to the top of the file and see exactly what names are being imported and where they're coming from (I'm talking about Python, of course, but the same reasoning could apply for C++).

贱贱哒 2024-07-20 16:57:59

Python 认为“显式优于隐式”。
(在 python 解释器中输入import this

另外,假设我正在阅读某人的代码。 也许这是你的代码; 也许这是我六个月前的代码。 我看到对 bar() 的引用。 函数从哪里来? 我可以在文件中查找 def bar(),但如果找不到它,那怎么办? 如果 python 通过导入自动查找第一个可用的 bar() ,那么我必须搜索导入的每个文件才能找到它。 多么痛苦啊! 如果函数查找通过导入层次结构递归会怎样?

我宁愿看到 zomg.bar(); 它告诉我该函数来自哪里,并确保在代码更改时我始终得到相同的函数(除非我更改 zomg 模块)。

Python takes the view that 'explicit is better than implicit'.
(type import this into a python interpreter)

Also, say I'm reading someone's code. Perhaps it's your code; perhaps it's my code from six months ago. I see a reference to bar(). Where did the function come from? I could look through the file for a def bar(), but if I don't find it, what then? If python is automatically finding the first bar() available through an import, then I have to search through each file imported to find it. What a pain! And what if the function-finding recurses through the import heirarchy?

I'd rather see zomg.bar(); that tells me where the function is from, and ensures I always get the same one if code changes (unless I change the zomg module).

撩发小公举 2024-07-20 16:57:59

问题在于抽象和重用:你真的不知道未来是否会出现任何歧义

例如,在项目中设置不同的库只是为了发现它们都有自己的字符串类实现(称为“string”),这是很常见的。
如果库没有封装在单独的命名空间中,编译器就会抱怨存在歧义。

然后,通过指定您想要在每个特定指令或上下文(阅读:作用域)中使用的实现(如标准 std::string )来避免这种歧义,这是一种令人愉快的乐趣。

如果您认为这在特定上下文中是显而易见的(请阅读:在 c++ 中的特定函数或 .cpp 中,在 python 中.py 文件 - 永远不要在 C++ 头文件中)您只需表达自己并说“它应该是显而易见的”,添加“使用命名空间”指令(或 import *)。 直到编译器抱怨,因为它不是。

如果您在特定范围内使用 using,则根本不会违反 DRY 规则。

The problem is about abstraction and reuse : you don't really know if there will not be any future ambiguity.

For example, It's very common to setup different libraries in a project just to discover that they all have their own string class implementation, called "string".
You compiler will then complain that there is ambiguity if the libraries are not encapsulated in separate namespaces.

It's then a delightful pleasure to dodge this kind of ambiguity by specifying wich implementation (like the standard std::string one) you wants to use at each specific instruction or context (read : scope).

And if you think that it's obvious in a particular context (read : in a particular function or .cpp in c++, .py file in python - NEVER in C++ header files) you just have to express yourself and say that "it should be obvious", adding the "using namespace" instruction (or import *). Until the compiler complain because it is not.

If you use using in specific scopes, you don't break the DRY rule at all.

黯然 2024-07-20 16:57:59

有些语言的编译器试图“做正确的事”——我想到的是 Algol 和 PL/I。 它们不再存在的原因是编译器非常不擅长做正确的事情,但非常擅长做错误的事情,只要有一半的机会!

There have been languages where the compiler tried to "do the right thing" - Algol and PL/I come to mind. The reason they are not around anymore is that compilers are very bad at doing the right thing, but very good at doing the wrong one, given half a chance!

唯憾梦倾城 2024-07-20 16:57:59

该规则力求的理想目标是使创建可重用组件变得容易 - 如果您重用组件,您只是不知道哪些符号将在客户端使用的其他命名空间中定义。 因此,该规则迫使您对于您尚不了解的进一步定义明确您的意图。

然而,C++ 还没有达到这个理想,主要是因为 Koenig 查找。

The ideal this rule strives for is to make creating reusable components easy - and if you reuse your component, you just don't know which symbols will be defined in other namespaces the client uses. So the rule forces you to make your intention clear with respect to further definitions you don't know about yet.

However, this ideal has not been reached for C++, mainly because of Koenig lookup.

櫻之舞 2024-07-20 16:57:59

这真的是正确的事情吗?

如果我有两种类型 ::bat 和 ::foo::bar

我想引用 bat 类型但不小心按了 r 键而不是 t (它们彼此相邻)怎么办?

编译器在每个命名空间中搜索以找到 ::foo::bar 而不给我任何警告是“正确的事情”吗?

或者,如果我在我的代码库中使用“bar”作为“::foo::bar”类型的简写,会怎么样。
然后有一天,我添加了一个定义 ::bar 数据类型的库。 突然之间出现了一种以前没有的歧义。 突然间,“正确的事情”变成了错误。

在这种情况下,编译器要做的正确事情是假设我的意思是我实际编写的类型。 如果我编写没有命名空间前缀的 bar,它应该假设我引用的是全局命名空间中的类型 bar。 但如果它在我们假设的场景中这样做,它会改变我的代码引用的类型,甚至不会提醒我。

或者,它可能会给我一个错误,但是来吧,这太荒谬了,因为即使使用当前的语言规则,这里也不应该有歧义,因为其中一种类型隐藏在我没有的命名空间中指定,所以不应该考虑。

另一个问题是编译器可能不知道还存在哪些其他类型。 在 C++ 中,定义的顺序很重要。

在 C# 中,类型可以在单独的程序集中定义,并在代码中引用。 编译器如何知道另一个具有相同名称的类型不存在于另一个程序集中,只是存在于不同的命名空间中? 它如何知道稍后不会将其添加到另一个程序集中?

正确的做法是做那些给程序员带来最少麻烦的事情。 根据不完整的数据对程序员进行事后猜测通常不是正确的做法。

大多数语言都提供了多种工具来避免指定名称空间。

在 C++ 中,您有“使用命名空间 foo”以及 typedef。 如果您不想重复命名空间前缀,那就不要这样做。 使用该语言提供的工具,这样您就不必这样做。

Is it really the right thing?

What if I have two types ::bat and ::foo::bar

I want to reference the bat type but accidentally hit the r key instead of t (they're right next to each others).

Is it "the right thing" for the compiler to then go searching through every namespace to find ::foo::bar without giving me even a warning?

Or what if I use "bar" as shorthand for the "::foo::bar" type all over my codebase.
Then one day I include a library which defines a ::bar datatype. Suddenly an ambiguity exists where there was none before. And suddenly, "the right thing" has become wrong.

The right thing for the compiler to do in this case would be to assume I meant the type I actually wrote. If I write bar with no namespace prefix, it should assume I'm referring to a type bar in the global namespace. But if it does that in our hypothetical scenario, it'll change what type my code references without even alerting me.

Alternatively, it could give me an error, but come on, that'd just be ridiculous, because even with the current language rules, there should be no ambiguity here, since one of the types is hidden away in a namespace I didn't specify, so it shouldn't be considered.

Another problem is that the compiler may not know what other types exist. In C++, the order of definitions matters.

In C#, types can be defined in separate assemblies, and referenced in your code. How does the compiler know that another type with the same name doesn't exist in another assembly, just in a different namespace? How does it know that one won't be added to another assembly later on?

The right thing is to do what gives the programmer the fewest nasty surprises. Second-guessing the programmer based on incomplete data is generally not the right thing to do.

Most languages give you several tools to avoid having to specify the namespace.

In c++, you have "using namespace foo", as well as typedefs. If you don't want to repeat the namespace prefix, then don't. Use the tools made available by the language so you don't have to.

怎樣才叫好 2024-07-20 16:57:59

这一切都取决于你对“正确的事情”的定义。 如果只有一个匹配项,编译器猜测您的意图是否正确?

双方都有论据。

This all depends on your definition of "right thing". Is it the right thing for the compiler to guess your intention if there's only one match?

There are arguments for both sides.

烟花肆意 2024-07-20 16:57:59

有趣的问题。 就 C++ 而言,正如我所见,如果编译器在发生冲突时立即标记错误,则可能导致的唯一问题是:

所有 C++ 命名空间的自动查找将消除隐藏的能力库代码内部部分的名称。

库代码通常包含永远不会对“外部世界”可见的部分(类型、函数、全局变量)。 C++ 之所以有未命名的命名空间,是为了避免“内部部分”堵塞全局命名空间,即使这些库命名空间是使用 using namespace xyz; 显式导入的。

示例:假设 C++ 确实进行了自动查找,并且 C++ 标准库的特定实现包含一个内部辅助函数 std::helper_func()。 假设用户 Joe 使用不包含 std::helper_func()不同库实现开发了一个包含函数 joe::helper_func() 的应用程序code>,并使用对 helper_func() 的非限定调用来调用他自己的方法。 现在,Joe 的代码可以在他的环境中正常编译,但任何其他尝试使用第一个库实现来编译该代码的用户都会遇到编译器错误消息。 因此,要使 Joe 的代码可移植,首先需要插入适当的 using 声明/指令或使用完全限定的标识符。 换句话说,自动查找对于可移植代码来说没有任何好处。

诚然,这似乎不是一个经常出现的问题。 但是,由于输入显式 using 声明/指令(例如 using namespace std;)对于大多数人来说并不是什么大问题,因此完全解决了这个问题,并且是可移植开发所必需的不管怎样,使用它们(呵呵)似乎是一种明智的做事方式。

注意:正如 Klaim 指出的,您在任何情况下都不会想要依赖头文件内的自动查找,因为这会立即阻止您的模块在以下位置使用:与任何包含冲突名称的模块同时发生。 (这只是为什么你不在 C++ 中的头文件中使用 using namespace xyz; 的逻辑扩展。)

Interesting question. In the case of C++, as I see it, provided the compiler flagged an error as soon as there was a conflict, the only problem this could cause would be:

Auto-lookup of all C++ namespaces would remove the ability to hide the names of internal parts of library code.

Library code often contains parts (types, functions, global variables) that are never intended to be visible to the "outside world." C++ has unnamed namespaces for exactly this reason -- to avoid "internal parts" clogging up the global namespace, even when those library namespaces are explicitly imported with using namespace xyz;.

Example: Suppose C++ did do auto-lookup, and a particular implementation of the C++ Standard Library contained an internal helper function, std::helper_func(). Suppose a user Joe develops an application containing a function joe::helper_func() using a different library implementation that does not contain std::helper_func(), and calls his own method using unqualified calls to helper_func(). Now Joe's code will compile fine in his environment, but any other user who tries to compile that code using the first library implementation will hit compiler error messages. So the first thing required to make Joe's code portable is to either insert the appropriate using declarations/directives or use fully qualified identifiers. In other words, auto-lookup buys nothing for portable code.

Admittedly, this doesn't seem like a problem that's likely to come up very often. But since typing explicit using declarations/directives (e.g. using namespace std;) is not a big deal for most people, solves this problem completely, and would be required for portable development anyway, using them (heh) seems like a sensible way to do things.

NOTE: As Klaim pointed out, you would never in any circumstances want to rely on auto-lookup inside a header file, as this would immediately prevent your module from being used at the same time as any module containing a conflicting name. (This is just a logical extension of why you don't do using namespace xyz; inside headers in C++ as it stands.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文