我听说过使用名称空间std;这是不好的做法,我应该直接使用std::cout和std::cin。这是为什么?声明与std命名空间中的某个变量同名的变量是否有风险?


当前回答

这是个案。我们希望在软件的生命周期内最大限度地降低软件的“总拥有成本”。声明“using namespace std”有一定的代价,但不使用它也有易读性的代价。

人们正确地指出,当使用它时,当标准库引入新的符号和定义时,您的代码将停止编译,您可能被迫重命名变量。然而,这可能是一个很好的长期目标,因为如果您出于某种令人惊讶的目的使用关键字,未来的维护人员将暂时感到困惑或分心。

你不希望有一个名为vector的模板,比如说,它不是其他人都知道的向量。因此,C++库中引入的新定义的数量非常少,可能根本找不到。必须进行这种更改是有代价的,但代价并不高,而且通过不将std符号名称用于其他目的而获得的清晰性来抵消。

考虑到类、变量和函数的数量,在每一个上声明std::可能会使代码出错50%,并使您更难理解。一个算法或方法中的一个步骤可以在一屏代码上执行,现在需要前后滚动才能执行。这是一个真实的成本。可以说,这可能不是一个高成本,但那些否认它存在的人是缺乏经验的、教条的,或者根本就是错误的。

我提供以下规则:

std不同于所有其他库。这是每个人基本上都需要知道的一个库,在我看来,最好将其视为语言的一部分。一般来说,使用命名空间std是一个很好的例子,即使没有其他库。永远不要通过将此using放在头中来迫使编译单元(.cpp文件)的作者做出决定。始终将决定权交给编译单元的作者。即使在一个决定在任何地方使用名称空间std的项目中,也可能会有一些模块作为该规则的例外处理。尽管名称空间功能允许您有许多定义相同符号的模块,但这样做会令人困惑。请尽可能保持名称不同。即使不使用名称空间特性,如果您有一个名为foo的类,并且std引入了一个名foo的类型,那么长期来说重命名您的类可能会更好。使用名称空间的另一种方法是通过前缀手动命名符号。我有两个库,我用了几十年,实际上都是从C库开始的,每个符号的前缀都是“AK”或“SCWin”。一般来说,这就像避免使用“using”构造,但不使用双冒号。AK::foo()改为AKFoo()。它使代码更密集5-10%,更不冗长,唯一的缺点是,如果必须使用两个具有相同前缀的这样的库,则会遇到很大的麻烦。注意,X Window库在这方面非常出色,只是它们忘记了使用一些#定义:TRUE和FALSE应该是XTRUE和XFALSE,这与Sybase或Oracle产生了命名空间冲突,它们同样使用了TRUE和FALSE,但值不同!(在数据库的情况下为ASCII 0和1!)这样做的一个特殊优点是,它似乎适用于预处理器定义,而C++使用/命名空间系统不处理它们。这样做的一个好处是,它提供了从项目的一部分到最终成为图书馆的有机坡度。在我的一个大型应用程序中,所有的窗口类都以Win为前缀,所有的信号处理模块都以Mod为前缀等等。这些模块中的任何一个都不太可能被重用,因此将每个组放入一个库中没有实际的好处,但几秒钟后就可以看出项目是如何分解为子项目的。

其他回答

“为什么在C++中‘使用命名空间std;’被认为是一种糟糕的做法?”

我反过来说:为什么有些人认为额外键入五个字符很麻烦?

例如,考虑编写一个数字软件。当“vector”是问题领域最重要的概念之一时,我为什么还要考虑将通用的“std::vector”缩减为“vector”来污染我的全局命名空间?

如果您导入了右侧的头文件,您的全局范围中会突然出现十六进制、左侧、加号或count等名称。如果您不知道std::包含这些名称,这可能会令人惊讶。如果您也尝试在本地使用这些名称,可能会导致一些混淆。

如果所有的标准内容都在自己的命名空间中,那么就不必担心与代码或其他库的名称冲突。

有一个非常简单的答案:这是防御性编程。您知道,使用名称空间std;-可以使std::size_t、std::cout等的使用更容易一些我希望您不必相信这样的指令在标题中没有位置!然而,在翻译单元中,您可能会受到诱惑。。。

作为std命名空间一部分的类型、类等随着每次C++修订而增加。如果放松std::限定符,可能会有太多的歧义。放松std中经常使用的名称的限定符是完全合理的,例如,使用std::fprintf;,或者更可能的是:使用std::size_t;-但是,除非这些已经是语言的很好理解的部分(或者特别是C库的std包装),否则只使用限定符。

当您可以使用typedef并结合自动和decltype推理时,从可读性/可维护性的角度来看,实际上没有什么好处。

这可能比Greg写的更糟!

Library Foo 2.0可能会引入一个函数Qux(),它无疑比多年来代码调用的bar::Qux)更适合您对Qux的某些调用。然后你的代码仍然可以编译,但它会默默地调用错误的函数,上帝知道会发生什么。这是最糟糕的事情了。

请记住,std名称空间有大量的标识符,其中许多是非常常见的标识符(例如列表、排序、字符串、迭代器等),这些标识符也很可能出现在其他代码中。

如果你认为这不太可能:在我给出这个答案大约半年后,在Stack Overflow上有一个问题(由于省略了std::前缀而调用了错误的函数)。这是这一问题的另一个最近的例子。所以这是一个真正的问题。


这里还有一个数据点:很多很多年前,我还发现必须在标准库中的所有内容前面加上std::,这很烦人。然后我在一个项目中工作,一开始就决定禁止使用指令和声明,但函数作用域除外。猜猜怎么了?我们中的大多数人花了几周的时间才习惯写前缀,再过几周,我们中的大部分人甚至同意这实际上使代码更可读。这是有原因的:你喜欢较短或较长的散文是主观的,但前缀客观上增加了代码的清晰度。不仅是编译器,您也会发现更容易看到引用的是哪个标识符。

在十年内,该项目发展到拥有数百万行代码。由于这些讨论一次又一次出现,我一度很好奇项目中实际使用(允许)函数范围的频率。我查阅了它的来源,只找到了一两打使用它的地方。对我来说,这表明,一旦尝试过,即使在允许使用std::的地方,开发人员也不会觉得每100kLoC使用一次指令就足够痛苦。


一句话:明确地给每件事加上前缀不会造成任何伤害,只需要很少的时间就可以习惯,而且有客观的优势。特别是,它使编译器和人类读者更容易解释代码,这可能是编写代码时的主要目标。

短版本:不要在头文件中使用全局using声明或指令。可以在实现文件中使用它们。以下是Herb Sutter和Andrei Alexandrescu在C++编码标准中对这个问题的看法(我用粗体强调):

总结命名空间using是为了方便您,而不是为了强加给他人:不要在#include指令之前编写using声明或using指令。推论:在头文件中,不要使用指令或声明编写命名空间级别;相反,显式命名空间限定所有名称。(第二条规则与第一条规则相同,因为标头永远无法知道后面可能出现的其他标头#includes。)讨论简而言之:在#include指令之后,你可以也应该在你的实现文件中自由地使用命名空间使用声明和指令,并对此感到满意。尽管重复了相反的断言,但使用命名空间的声明和指令并不是邪恶的,它们不会破坏命名空间的目的。相反,它们使名称空间可用。