为什么“使用命名空间std;”被认为是不好的做法?

我听说using namespace std;是不好的做法,我应该直接使用std::coutstd::cin。这是为什么?它是否有风险声明与std命名空间中的某些东西共享相同名称的变量?

1118035 次浏览

另一个原因是惊喜。

如果我看到cout << blah,而不是std::cout << blah,我想:这是什么cout?它是正常的cout吗?它是什么特别的东西吗?

考虑两个名为Foo和Bar的库:

using namespace foo;using namespace bar;

一切正常,你可以从Foo调用Blah(),从Bar调用Quux()没有问题。但是有一天你升级到新版本的Foo 2.0,它现在提供了一个名为Quux()的函数。现在你遇到了冲突:Foo 2.0和Bar都将Quux()导入你的全局命名空间。这需要一些努力来修复,特别是如果函数参数恰好匹配。

如果你使用了foo::Blah()bar::Quux(),那么foo::Quux()的引入将是一个非事件。

这都是关于管理复杂性的。使用命名空间会把你不想要的东西拉进来,从而可能使调试更难(我说可能)。到处使用std:: 更难阅读(更多的文本和所有这些)。

马的课程-管理你的复杂性如何你最好的和感觉能够。

这取决于它的位置。如果它是一个公共标头,那么您通过将其合并到全局命名空间中来减少命名空间的值。请记住,这可能是制作模块全局的一种巧妙方法。

考虑

// myHeader.h#include <sstream>using namespace std;

// someoneElses.cpp/h#include "myHeader.h"
class stringstream {  // Uh oh};

请注意,这是一个简单的例子。如果你有20个包含和其他导入的文件,你将有大量的依赖项来解决问题。更糟糕的是,根据冲突的定义,你可以在其他模块中得到不相关的错误。

它并不可怕,但如果不在头文件或全局命名空间中使用它,您会省去麻烦。在非常有限的范围内这样做可能没问题,但我从来没有遇到过输入额外的五个字符来澄清我的函数来自哪里的问题。

using namespace放在类的头文件中的问题在于,它会迫使任何想要使用类的人(通过包含头文件)也“使用”(即在其中查看所有内容)其他名称空间。

但是,您可以随意在您的(私有)*. cpp文件中放置使用语句。


注意,有些人不同意我这样说的“自由”——因为尽管cpp文件中的using语句比头文件中的更好(因为它不会影响包含你的头文件的人),他们认为它仍然不是(因为取决于代码,它可能会使类的实现更难维护)。

使用指令用于遗留C++代码并简化向命名空间的过渡,但您可能不应该定期使用它,至少不应该在新的C++代码中使用它。

FAQ建议两种选择:

  • 使用声明:

    using std::cout; // a using-declaration lets you use cout without qualificationcout << "Values:";
  • Just typing std::

    std::cout << "Values:";

如果您导入了正确的头文件,您的全局作用域中突然出现了#0#1#2#3这样的名称。如果您不知道std::包含这些名称,这可能会令人惊讶。如果您还尝试在本地使用这些名称,它可能会导致相当大的混乱。

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

我不认为在任何情况下这都是不好的做法,但你在使用它时需要小心。如果你正在编写一个库,你可能应该使用命名空间的范围解析运算符来防止你的库与其他库发生冲突。对于应用程序级别的代码,我不认为它有什么问题。

  1. 您需要能够阅读由与您有不同风格和最佳实践意见的人编写的代码。

  2. 如果你只使用cout,没有人会感到困惑。但是当你有很多命名空间在周围飞来飞去,并且你看到这个类并且你不完全确定它的作用时,显式命名空间就像某种注释一样。乍一看,你可以看到,“哦,这是一个文件系统操作”或“那是做网络的东西”。

它可以得到更糟糕的比什么格雷格写道

Library Foo 2.0可以引入一个函数Quux(),它无疑比你的代码多年来调用的bar::Quux()更好地匹配你对Quux()的一些调用。然后是你的代码仍然编译,但它会静默调用错误的函数,天知道会做什么。这大概是最糟糕的事情了。

请记住,std命名空间有大量标识符,其中许多是非常常见标识符(想想listsortstringiterator等),这些标识符也很可能出现在其他代码中。

如果你认为这不太可能:在我给出这个答案大约半年后,Stack Overflow上的一个问题几乎完全发生了这种情况(由于省略了std::前缀而调用了错误的函数)。所以这是一个真正的问题。


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

十年间,这个项目发展到拥有数百万行代码。由于这些讨论一次又一次地出现,我曾经很好奇(允许的)函数作用域using在项目中实际使用的频率有多高。我查阅了它的来源,只找到了一到二十几个使用它的地方。对我来说,这表明,一旦尝试过,开发人员不会发现std::足够痛苦,甚至在允许使用的地方每100 kLoC使用一次指令。


底线:显式地为所有内容加上前缀没有任何害处,只需要很少的习惯时间,并且具有客观优势。特别是,它使代码更容易被编译器和人类读者解释——这可能应该是编写代码时的主要目标。

我也认为这是一个不好的做法。为什么?只是有一天我认为命名空间的功能是划分东西,所以我不应该把所有东西都扔进一个全局包里。

但是,如果我经常使用'cout'和'cin',我会在. cpp文件中写入:using std::cout; using std::cin;(从不在头文件中,因为它与#include一起传播)。我认为没有一个理智的人会命名流coutcin。;)

不应该在全局范围内使用using指令,尤其是在头文件中。但是,在某些情况下,即使在头文件中也是合适的:

template <typename FloatType> inlineFloatType compute_something(FloatType x){using namespace std; // No problem since scope is limitedreturn exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4));}

这比显式限定(std::sinstd::cos…)要好,因为它更短,并且能够使用用户定义的浮点类型(通过参数相关查找(ADL))。

我最近遇到了一个关于Visual Studio 2010的投诉。事实证明,几乎所有的源文件都有这两行:

using namespace std;using namespace boost;

许多Boost功能将进入C++0x标准,而Visual Studio 2010有许多C++0x功能,因此突然之间这些程序无法编译。

因此,避免using namespace X;是一种面向未来的形式,一种确保对正在使用的库和/或头文件的更改不会破坏程序的方法。

有经验的程序员使用任何可以解决他们问题的东西,避免任何会产生新问题的东西,并且他们避免使用头文件级的use-指令正是出于这个原因。

有经验的程序员也尽量避免在源文件中完全限定名称。这样做的一个次要原因是,当更少的代码就足够了除非有充分的理由时编写更多的代码并不优雅。这样做的一个主要原因是关闭依赖参数的查找(ADL)。

这些充分的理由是什么?有时程序员明确地想要关闭ADL,有时他们想要消除歧义。

以下是OK:

  1. 函数实现中的函数级使用指令和使用声明
  2. 源文件中的源文件级使用声明
  3. (有时)源文件级使用指令

不要在全球范围内使用它

只有在全球使用时才被认为是“坏的”。因为:

  • 你弄乱了你正在编程的命名空间。
  • 当您使用许多using namespace xyz;时,读者很难看到特定标识符来自哪里。
  • 无论对源代码的其他读者来说是什么,对最频繁的读者来说都是如此:你自己。一两年后回来看看…
  • 如果你只谈论using namespace std;,你可能不知道你得到的所有东西-当你添加另一个#include或移动到一个新的C++版本时,你可能会得到你不知道的名称冲突。

您可以在本地使用

继续在本地(几乎)自由地使用它。这当然会阻止您重复std::-重复也不好。

在本地使用它的成语

C++03中,有一个习惯用法--样板代码--用于为您的类实现swap函数。有人建议您实际使用本地using namespace std;-或至少using std::swap;

class Thing {int    value_;Child  child_;public:// ...friend void swap(Thing &a, Thing &b);};void swap(Thing &a, Thing &b) {using namespace std;      // make `std::swap` available// swap all membersswap(a.value_, b.value_); // `std::stwap(int, int)`swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`}

这有以下魔法:

  • 编译器将为value_选择std::swap,即void std::swap(int, int)
  • 如果您实现了重载void swap(Child&, Child&),编译器将选择它。
  • 如果没有有重载,编译器将使用void std::swap(Child&,Child&)并尝试最好地交换这些。

对于C++11,没有理由再使用这种模式了。std::swap的实现被更改为查找潜在的重载并选择它。

是的,命名空间很重要。在我的项目中,我需要将一个var声明导入我的源代码,但在编译它时,它与另一个第三方库冲突。

最后,我不得不通过其他方式解决它,使代码不那么清晰。

对于不合格的导入标识符,您需要像grep这样的外部搜索工具来找出标识符的声明位置。这使得推理程序正确性变得更加困难。

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

我反过来说:为什么一些人认为多打五个字符很麻烦?

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

为了回答你的问题,我实际上是这样看的:很多程序员(不是全部)调用命名空间std。因此,应该养成不使用与命名空间std中的名称相同或使用相同名称的东西的习惯。这是一个很大的授予,但与严格来说可以想出的可能连贯的单词和假名的数量相比,并没有那么多。

我的意思是,说“不要依赖于这个存在”只是让你依赖它不存在。你经常会遇到借用代码片段和不断修复它们的问题。只要把你用户定义的和借用的东西保持在应该的有限范围内,并且非常节省全局变量(老实说,全局变量几乎总是“现在编译,稍后理智”目的的最后手段)。真的,我认为这是你老师的糟糕建议,因为使用std对“cout”和“std::cout”都有效,但不使用std只会对“std::cout”有效。你不会总是有幸编写所有自己的代码。

注意:在你真正了解一点编译器是如何工作的之前,不要太关注效率问题。有了一点编码经验,你不必学习那么多,就会意识到他们能够在多大程度上将好的代码泛化成简单的东西。每一点都像你用C写的整个代码一样简单。好的代码只是像它需要的那样复杂。

我同意它不应该全局使用,但在本地使用也不是那么邪恶,就像在namespace中一样。这是《C++编程语言》中的一个例子:

namespace My_lib {
using namespace His_lib; // Everything from His_libusing namespace Her_lib; // Everything from Her_lib
using His_lib::String; // Resolve potential clash in favor of His_libusing Her_lib::Vector; // Resolve potential clash in favor of Her_lib
}

在此示例中,我们解决了由其组成引起的潜在名称冲突和歧义。

在那里显式声明的名称(包括像His_lib::String这样通过using-声明声明的名称)优先于由using-Directive(using namespace Her_lib)在另一个范围内访问的名称。

我认为使用当地全球应该取决于应用程序。

因为,当我们在本地使用库时,有时代码会变得一团糟。可读性会降低。

因此,只有当存在冲突的可能性时,我们才应该在本地使用库。

我不是一个更有经验的人。所以,让我知道如果我错了。

同时使用多个命名空间显然是一个灾难的秘诀,但在我看来,使用只是命名空间std和只有命名空间std并不是什么大不了的事情,因为重新定义只能由你自己的代码发生。

因此,只需将它们视为保留名称,例如“int”或“class”,就是这样。

人们应该停止对它如此挑剔。你的老师一直是对的。只需使用一个命名空间;这就是首先使用命名空间的全部意义。你不应该同时使用多个命名空间。除非是你自己的。所以,重新定义不会发生。

很高兴看到代码并知道它的作用。如果我看到std::cout,我知道那是std库的cout流。如果我看到cout,那么我不知道。它可以std库的cout流。或者在同一个函数中可能有int cout = 0;高十行。或者该文件中名为coutstatic变量。它可以是任何东西。

现在以一百万行代码为例,这不是特别大,你正在寻找一个bug,这意味着你知道这一百万行中有一行没有做它应该做的事情。cout << 1;可以读取名为coutstatic int,将其向左移动一位,然后丢弃结果。寻找bug,我必须检查一下。你能看出我真的很喜欢看到std::cout吗?

如果你是一名教师,从来不用以编写和维护任何代码为生,那么这似乎是一个非常好的主意。我喜欢看到代码(1)我知道它是做什么的;(2)我相信编写它的人知道它是做什么的。

我同意其他人的观点——它要求名称冲突、模棱两可,然后事实是它不那么明确。虽然我可以看到using的使用,但我个人的偏好是限制它。我也强烈考虑其他人指出的:

如果您想找到一个可能是相当常见的名称的函数名称,但您只想在std命名空间中找到它(或者相反-您想更改命名空间std没有的所有调用,命名空间X,…),那么您建议如何做到这一点?

你可以写一个程序来做这件事,但是花时间在你的项目本身而不是写一个程序来维护你的项目不是更好吗?

就我个人而言,我其实不介意std::这个前缀。比起没有它,我更喜欢它的外观。我不知道这是因为它明确地告诉我“这不是我的代码……我正在使用标准库”,还是因为它是别的什么,但我认为它看起来更好。这可能很奇怪,因为我最近才开始C++(使用和仍然使用C和其他语言的时间要长得多,C是我一直以来最喜欢的语言,就在汇编之上)。

还有一件事,虽然它与上述以及其他人指出的有些相关。虽然这可能是不好的做法,但我有时会将std::name保留为标准库版本,并为特定于程序的实现保留名称。是的,这确实会让你痛苦,但这一切都可以归结为我从零开始这个项目,并且我是它的唯一程序员。示例:我重载std::string并将其命名为string。我有有用的补充。我这样做的部分原因是我的C和Unix(+Linux)倾向于小写名称。

除此之外,你可以有命名空间别名。这里有一个可能没有被引用的有用的例子。我使用C++11标准,特别是libstdc++。嗯,它没有完整的std::regex支持。当然,它编译,但它抛出了一个异常,就像程序员的错误一样。但它缺乏实现。

所以我是这样解决的。安装Boost的regex,并将其链接进来。然后,我执行以下操作,以便当libstdc++完全实现它时,我只需要删除这个块,代码保持不变:

namespace std{using boost::regex;using boost::regex_error;using boost::regex_replace;using boost::regex_search;using boost::regex_match;using boost::smatch;namespace regex_constants = boost::regex_constants;}

我不会争论这是否是一个坏主意。然而,我将争辩说,它使项目保持干净,同时使其具体:没错,我必须使用Boost,我使用它就像libstdc++最终会拥有它一样。是的,启动自己的项目并从一开始就从标准(…)开始,对帮助维护、开发和项目相关的所有事情都有很大帮助!

只是为了澄清一些事情:我实际上并不认为在STL中故意使用类的名称/任何东西来代替. string是个例外(忽略第一个,上面,或者第二个,如果你必须的话,双关语)因为我不喜欢'String'的想法。

事实上,我仍然非常偏向于C和C++。保留细节,我所做的大部分工作更适合C(但这是一个很好的练习,也是让自己a.学习另一种语言的好方法,b.尽量不要对对象/类/等有更少的偏见,也许更好的说法是不那么封闭,不那么傲慢,更容易接受。)。但有用的是一些人已经建议的:我确实使用列表(它相当通用,不是吗?),并对(同样的事情)进行排序,以命名两个如果我要做using namespace std;会导致名称冲突的名称,因此为此,我更喜欢具体,控制并知道如果我打算将其作为标准用途,那么我将不得不指定它。简单地说:不允许假设。

至于让Boost的regex成为std的一部分。我这样做是为了将来的集成,我再次承认这是偏见,我不认为它像boost::regex:: ...那样丑陋。事实上,这对我来说是另一回事。C++中有很多东西在外观和方法上我还没有完全接受(另一个例子:可变参数模板与var参数[尽管我承认可变参数模板非常非常有用!])。即使是那些我接受的东西也很困难,我仍然有问题。

简短版本:不要在头文件中使用全局using声明或指令。随意在实现文件中使用它们。以下是HerbSutter安德烈·亚历山大斯库C++编码标准中对这个问题的看法(粗体强调是我的):

总结

命名空间的使用是为了您的方便,而不是为了您对其他人造成伤害:永远不要在#包含指令之前写一个使用声明或使用指令。

推论:在头文件中,不要使用指令或声明编写namespace-level;相反,显式地命名空间限定所有名称。(第二条规则遵循第一条规则,因为头文件永远不会知道在它们之后可能会出现其他标题#包含的内容。)

讨论

简而言之:您可以并且应该在#包括指令之后的实现文件中自由地使用声明和指令来使用命名空间,并对此感到满意。尽管反复断言相反,但使用声明和指令的命名空间并不邪恶,它们不会破坏命名空间的目的。相反,它们使命名空间可用

一个例子,其中using namespace std抛出编译错误,因为计数的歧义,这也是一个函数在算法库中。

#include <iostream>#include <algorithm>
using namespace std;
int count = 1;int main() {cout << count << endl;}

我同意这里的其他人,但我想解决有关易读性的问题-您可以通过简单地在文件,函数或类声明的顶部使用typedef来避免所有这些。

我通常在类声明中使用它,因为类中的方法倾向于处理类似的数据类型(成员),而typedef是分配在类上下文中有意义的名称的机会。这实际上有助于类方法定义的易读性。

// Headerclass File{typedef std::vector<std::string> Lines;Lines ReadLines();}

并在执行中:

// .cppLines File::ReadLines(){Lines lines;// Get them...return lines;}

而不是:

// .cppvector<string> File::ReadLines(){vector<string> lines;// Get them...return lines;}

或:

// .cppstd::vector<std::string> File::ReadLines(){std::vector<std::string> lines;// Get them...return lines;}

命名空间是一个命名的范围。命名空间用于对相关声明进行分组并保持单独项目分开。例如,两个单独开发的库可能使用相同的名称来引用不同的项,但用户仍然可以同时使用两者:

namespace Mylib{template<class T> class Stack{ /* ... */ };// ...}
namespace Yourlib{class Stack{ /* ... */ };// ...}
void f(int max) {Mylib::Stack<int> s1(max); // Use my stackYourlib::Stack    s2(max); // Use your stack// ...}

重复命名空间名称可能会分散读者和作者的注意力。因此,这是可能的声明来自特定命名空间的名称无需明确限定即可使用。例如:

void f(int max) {using namespace Mylib; // Make names from Mylib accessibleStack<int> s1(max); // Use my stackYourlib::Stack s2(max); // Use your stack// ...}

命名空间为管理不同的库和不同版本的代码提供了一个强大的工具。特别是,它们为程序员提供了如何显式引用非本地名称的替代方案。

来源:C++编程语言概述作者:Bjarne Stroustrup

以下是一个示例,显示了using namespace std;如何导致名称冲突问题:

无法在C++中定义全局变量

在示例中,一个非常通用的算法名称(std::count)与一个非常合理的变量名称(count)冲突。

根据我的经验,如果你有多个库使用cout,但出于不同的目的,你可能会使用错误的cout

例如,如果我键入using namespace std;using namespace otherlib;,而只键入cout(恰好两者都在),而不是std::cout(或'otherlib::cout'),您可能会使用错误的,并得到错误。使用std::cout更有效和高效。

一个具体的例子来澄清这个问题。假设您有两个库,foobar,每个库都有自己的命名空间:

namespace foo {void a(float) { /* Does something */ }}
namespace bar {...}

现在假设您在自己的程序中一起使用foobar,如下所示:

using namespace foo;using namespace bar;
void main() {a(42);}

在这一点上一切都很好。当你运行你的程序时,它会“做某事”。但是后来你更新了bar,假设它已经改变为:

namespace bar {void a(float) { /* Does something completely different */ }}

此时你会得到一个编译器错误:

using namespace foo;using namespace bar;
void main() {a(42);  // error: call to 'a' is ambiguous, should be foo::a(42)}

所以你需要做一些维护来澄清'a'意味着foo::a。这是不可取的,但幸运的是它很容易(只需在编译器标记为不明确的所有对a的调用之前添加foo::)。

但是想象一下另一种场景,其中bar更改为如下所示:

namespace bar {void a(int) { /* Does something completely different */ }}

此时,您对a(42)的调用突然绑定到bar::a而不是foo::a,并且不是做“某事”,而是做“完全不同的事情”。没有编译器警告或任何东西。您的程序只是默默地开始做一些与以前完全不同的事情。

当你使用命名空间时,你会冒着这样的风险,这就是为什么人们使用命名空间不舒服的原因。命名空间中的东西越多,冲突的风险就越大,所以人们使用命名空间std(由于该命名空间中的东西数量)可能比其他命名空间更不舒服。

归根结底,这是可写性与可靠性/可运维性之间的权衡。可读性也可能是一个因素,但我认为这两种方式都有争论。通常我会说可靠性和可运维性更重要,但在这种情况下,你将不断为相当罕见的可靠性/可运维性影响支付可写性成本。“最佳”权衡将决定你的项目和你的优先级。

这里有一个观点,我在其他任何答案中都没有找到:只使用一个命名空间。根据大多数答案,命名空间不好的主要原因是,你可以有冲突的函数名称,这可能会导致完全混乱。然而,如果你只使用一个命名空间,这不会发生。决定你将使用最多的是哪个库(也许是using namespace std;)并坚持下去。

人们可以把它想象成有一个不可见的库前缀-std::vector变成了vector。在我看来,这是两全其美的:一方面它减少了你必须做的键入量(正如命名空间所预期的那样),另一方面,它仍然需要你使用前缀来清晰和安全。如果有一个没有命名空间前缀的函数或对象-你知道它来自你声明的一个命名空间。

请记住,如果您决定全局使用一个-不要在本地使用其他名称空间。这又回到了其他答案,即本地命名空间通常比全局命名空间更有用,因为它们提供了多种便利。

老实说,对我来说,这就像讨论缩进的空格数量。

在标题中使用指令会造成损害。但是在C++文件中呢?也许如果你同时使用两个命名空间。但是如果你使用一个,它更多的是风格而不是真正的效率。

你知道为什么关于缩进的主题如此流行吗?任何人都可以说一些关于它的事情,听起来非常聪明和有经验。

这是一个不好的做法,通常被称为全局命名空间污染。当多个命名空间具有相同的带有签名的函数名称时,可能会出现问题,那么编译器决定调用哪个将是模棱两可的,当您使用std::cout之类的函数调用指定命名空间时,这一切都可以避免。希望这能有所帮助。:)

它不会使您的软件或项目性能变差。在源代码开头包含命名空间并不坏。using namespace std指令的包含因您的需求和开发软件或项目的方式而异。

namespace std包含C++标准函数和变量。当您经常使用C++标准函数时,此命名空间很有用。

如上所述页面

使用命名空间std的语句通常被认为是不好的此声明的替代方法是指定使用范围运算符(::)标识符所属的命名空间每次我们声明一个类型时。

看看这一意见

在源文件中使用“使用命名空间std”没有问题当你大量使用命名空间并确定什么都不会发生冲突

有些人说在源文件中包含using namespace std是一种不好的做法,因为您正在从该命名空间调用所有函数和变量。当你想定义一个与namespace std中包含的另一个函数同名的新函数时,你会重载该函数,并且它可能会由于编译或执行而产生问题。它不会像你期望的那样编译或执行。

如上所述页面

虽然语句使我们不必在任何时候输入std:: 我们希望访问std命名空间中定义的类或类型,它将整个std命名空间导入当前命名空间让我们举几个例子来理解为什么可能不是一件好事

现在在开发的后期阶段,我们希望使用另一个版本的在某个名为“foo”的库中自定义实现的cout(用于示例)

注意这里有一个歧义,cout指向哪个库?编译器可能会检测到这一点而无法编译程序。在最坏的情况下情况下,程序可能仍然编译,但调用了错误的函数,因为我们从未指定标识符属于哪个命名空间。

这是逐个案例。我们希望在软件的生命周期内最小化软件的“总拥有成本”。声明“使用命名空间std”有一些成本,但是没有使用它也有易读性成本。

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

比如说,你不会首先拥有一个名为向量的模板,因为它不是其他人都知道的向量。而且C++库中引入的新定义的数量足够少,可能根本不会出现。必须做这种改变会有成本,但成本并不高,并且被不将std符号名称用于其他目的所获得的清晰度所抵消。

考虑到类、变量和函数的数量,在每个类、变量和函数上都写上std::可能会让你的代码蓬松50%,让你更难理解。现在,一屏幕代码就能理解的算法或方法中的步骤需要来回滚动才能理解。这是一个真实的成本。可以说这不是很高的成本,但否认这一点的人就是缺乏经验、教条主义者,或者根本就是错误的。

我提供以下规则:

  1. std与所有其他库都不同。它是每个人基本上都需要知道的一个库,在我看来,最好将其视为语言的一部分。一般来说,即使没有其他库,也有using namespace std的绝佳案例。

  2. 永远不要通过将using放在标题中来将决定强加给编译单元(. cpp文件)的作者。始终将决定推迟到编译单元作者。即使在一个决定在任何地方使用using namespace std的项目中,也可能会发现一些最好作为该规则例外处理的模块。

  3. 尽管命名空间特性允许你有许多模块的符号定义相同,但这样做会让人困惑。尽可能保持名称不同。即使不使用命名空间特性,如果你有一个名为foo的类,而std引入了一个名为foo的类,无论如何,重命名你的类可能是更好的长期运行。

  4. 使用命名空间的另一种方法是通过前缀手动命名空间符号。我有两个几十年来一直使用的库,实际上都是从C库开始的,其中每个符号都以“AK”或“SCWin”为前缀。一般来说,这就像避免了“使用”构造,但你不会编写双冒号。AK::foo()而是AKFoo()。它使代码密度提高了5-10%,减少了冗长,唯一的缺点是如果你必须使用两个具有相同前缀的此类库,你会遇到大麻烦。请注意,X Window库在这方面非常出色,除了它们忘记了使用一些#定义:TRUE和FALSE应该是XTRUE和XFALSE,这与赛贝斯或Oracle设置了命名空间冲突,它们同样使用不同值的TRUE和FALSE!(在数据库的情况下是ASCII 0和1!)这样做的一个特殊优点是,它可以无缝地应用于预处理器定义,而C++using/namespace系统不能处理它们。这样做的一个很好的好处是,它提供了从项目的一部分到最终成为库的有机斜率。在我的一个大型应用程序中,所有窗口类都以Win为前缀,所有信号处理模块Mod,等等。这些中的任何一个都几乎没有被重用的机会,所以将每个组变成一个库没有实际的好处,但它在几秒钟内就清楚地表明了项目是如何分解为子项目的。

有一个非常简单的答案:这是防御性编程。您知道std::size_tstd::cout等的使用,可以可以通过using namespace std;变得更容易-我希望您不需要确信这样的指令在标头中没有位置!然而,在翻译单元中,您可能会受到诱惑……

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

当你可以使用typedef,结合autodecltype推理时,从易读性/可运维性的角度来看,真的没有什么可获得的。

#include <iostream>
using namespace std;
int main() {// There used to be// int left, right;// But not anymore
if (left != right)std::cout << "Excuse me, WHAT?!\n";}

那么,为什么呢?因为它引入与常用变量名重叠的标识符,并让此代码编译,将其解释为if (std::left != std::right)

PVS-Studio可以使用V1058诊断找到这样的错误:https://godbolt.org/z/YZTwhp(谢谢Andrey Karpov!!)。

ping cppcheck开发人员:您可能希望标记这个。这是一个doozy。

命名空间是为了避免命名冲突。C++是基于C的,C在函数和变量名称方面有很多问题,因为有时来自不同库的函数会发生冲突。所以库开发人员开始用库名称作为他们函数的前缀,如下所示:

#0

void libfoo_foo_foo_h_open(); // the name can be weird then even this one!

C++引入命名空间以简单的方式解决这个问题。

假设您有两个名为filewindow的库,分别处理文件和窗口以及以下代码:

#include <file.h>#include <window.h>
using namespace file;using namespace window;
void open() {...}

#0

namespace file {void open(); // What!}

#0

namespace window {void open(); // Oh no!}

上面的代码肯定会编译失败。

如果你不喜欢类型std::(只有5个字符),你可以这样做:(在头文件中不是一个好主意)

using s = std;

如果您仍然想在源文件中使用using namespace std;,那么您就会引发这个问题,我必须问你“命名空间的目的是什么?”。

为什么命名空间std使用?

C++有一个标准库,其中包含您在构建应用程序时使用的常见功能,例如容器,算法等。如果这些应用程序使用的名称是公开的,例如,如果他们全局定义了一个队列类,您将永远无法在没有冲突的情况下再次使用相同的名称。所以他们创建了一个命名空间,std来包含此更改。

原因1不使用:坏习惯

使用命名空间std的语句通常被认为是不好的做法。此语句的替代方法是在每次声明类型时使用范围运算符(::)指定标识符所属的命名空间。尽管该语句使我们在希望访问std命名空间中定义的类或类型时不必键入std:: ,但它会将整个std命名空间导入程序的当前命名空间。

原因2不使用:编译器变得混乱

可以在玩具程序中导入整个std库,但在生产级代码中,这很糟糕。使用命名空间std;使命名空间std中声明的每个符号都可以在没有命名空间限定符的情况下访问。

例如:

现在,假设您升级到C++的新版本,并且更多您不知道的新std命名空间符号被注入到您的程序中。您可能已经在程序中使用了这些符号。现在编译器将很难确定声明的符号是属于您自己的实现还是来自您不知道导入的命名空间。一些编译器会抛出错误。如果你不幸,编译器选择了错误的实现并编译它,这肯定会导致运行时崩溃。

命名空间污染效应:

虽然这种做法对于示例代码是可以的,但将整个std命名空间拉入全局命名空间并不好,因为它违背了命名空间的目的并可能导致名称冲突。这种情况称为命名空间污染。

只要您不在头文件中使用“使用命名空间std”,它就应该是完全正常的。

如果有命名冲突说提升,那么你不使用这个语句在特定的. cpp文件,但仍然保存每个人的眼睛和手指不重复'std::'十次在其他文件的每一行。

不幸的是,现在这是一个纯粹的宗教问题,每个人都更容易/更有成效地遵循一遍又一遍重复“圣d::”的既定模式。