为什么是“使用命名空间标准”;被认为是不良做法?


2639

其他人告诉我,写using namespace std;代码是错误的,应该直接使用std::coutstd::cin代替。

为什么被using namespace std;认为是不良做法?是效率低下还是冒着声明模棱两可的变量(与std命名空间中的函数具有相同名称的变量)的风险?它会影响性能吗?


512
别忘了您可以做:“使用std :: cout;”。这意味着您不必键入std :: cout,但不必同时引入整个std名称空间。
比尔


64
在头文件的文件作用域中使用“ using namespace std”特别糟糕。毕竟在文件范围内在源文件(* .cpp)中使用它并没有那么糟糕,因为它的作用仅限于单个翻译单元。在函数或类中使用它的问题更少,因为它的作用仅限于函数或类的范围。
sh-

5
我不鼓励用指令的使用,但具体的命名空间一样std::literals::chrono_literalsPoco::Data:KeywordsPoco::Units和东西,将处理文字或可读性的技巧。只要它在头文件或实现文件中。我认为在函数范围内可能还可以,但是除了文字和内容之外,它没有用。
Ludovic Zenohate Lagouardette'7

7
@Jon:与命名空间std无关。我的重点是放在“头文件的文件范围内”。提出建议:不要在头文件的文件范围内使用“使用命名空间”(std或其他)。可以在实现文件中使用它。抱歉,含糊不清。
sh-

Answers:


2229

这根本与性能无关。但是考虑一下:您正在使用两个名为Foo和Bar的库:

using namespace foo;
using namespace bar;

一切正常,您可以Blah()从Foo和Quux()Bar 轻松致电。但是有一天,您将升级到Foo 2.0的新版本,该版本现在提供了名为的功能Quux()。现在您有一个冲突:Foo 2.0和Bar都导入Quux()到全局名称空间中。这将需要花费一些时间来解决,特别是在功能参数碰巧匹配的情况下。

如果您使用foo::Blah()bar::Quux(),那么引入foo::Quux()将会是一件不可能的事。


435
我一直喜欢Python的“将big_honkin_name作为bhn导入”,因此您可以只使用“ bhn.something”而不是“ big_honkin_name.something”,这确实减少了键入。C ++有这样的东西吗?
paxdiablo

764
@Pax名称空间io = boost :: filesystem;
AraK

152
我认为这是“修复工作”,有些夸大其词。您将没有新的foo :: Quux的实例,因此只需使用bar :: Quux消除当前使用的所有歧义。
MattyT

289
有理智的人会创建一个类型不符合标准名称的库吗?
erikkallen

94
@TomA:问题#define在于它不会将自身限制为名称空间,而是践踏了整个代码库。您想要的是名称空间别名。
sbi

1390

我同意Greg撰写的所有内容,但我想补充一点:它甚至比Greg所说的还要糟糕!

Library Foo 2.0可以引入一个函数,Quux()Quux()bar::Quux()您多年调用的代码相比,对您的某些调用无疑具有更好的匹配。然后,您的代码仍然可以编译,但是它会静默地调用错误的函数,并且知道什么。事情会变得糟透了。

请记住,在std命名空间有万吨标识符,其中许多都是常见的(想想listsortstringiterator,等),这是非常有可能出现在其他的代码了。

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


这是另一个数据点:许多年前,我还发现必须在标准库中添加所有前缀,这很烦人std::。然后,我在一个项目中工作,从一开始就决定using禁止除功能范围之外的所有指令和声明。你猜怎么了?我们大多数人花了几周的时间来习惯于编写前缀,而又过了几周,我们大多数人甚至同意它实际上使代码更具可读性。这样做的原因是:您喜欢散文是短还是长是主观的,但是前缀客观地增加了代码的清晰度。不仅是编译器,而且您也发现查找引用的标识符更加容易。

在十年中,该项目发展成拥有几百万行代码。由于这些讨论一次又一次地出现,我曾经很好奇using过在项目中实际使用(允许的)功能范围的频率。我找到了它的来源,只发现了使用它的一两个地方。对我来说,这表明,一旦尝试,std::即使允许每100 kLoC使用一次,开发人员也不会感到痛苦,甚至无法使用指令。


底线:明确地为所有内容加上前缀不会造成任何损害,几乎不需要花什么时间,就具有客观优势。尤其是,它使代码更易于由编译器和人类读者解释,而这可能应该是编写代码时的主要目标。


140
它确实会严重损害您可以打包在一行中的代码的密度。您最终将以冗长的方式编写代码。这会降低可读性。就我个人而言,我认为较短(但不是太短)的代码往往更具可读性(因为要阅读的东西更少,而分散注意力的东西也更少)。
Lie Ryan 2010年

91
猜猜您在C ++拥有标准string类之前就错过了过去,而且似乎每个库都有自己的类。告诉您什么:我们将继续使用编写代码std::,并且可以grep -v std:: | vim在浏览代码时始终运行它们。或者,您可以告诉您的编辑器该std::关键字的颜色应与背景颜色相同。无论如何。
Mike DeSimone 2010年

79
我认为根本没有std::害处。它包含非常重要的信息(即“后续内容是标准库的一部分”,并且仍然是一个简短而紧凑的前缀。在大多数情况下,这根本没有问题。有时,您需要几行代码在这里你需要参考特定符号的std命名空间很多,然后using,声明在特定的范围内解决这个问题很好,但在一般情况下,这不是噪音,它传达有价值的信息除了要消除歧义。
jalf

146
每当我看到时std::,我都知道它来自于std::无需考虑的事物。如果我自己看到stringlistmap自己看到,我会有点奇怪。
Mateen Ulhaq 2011年

67
@LieRyan然后祝您好运,无需编写任何东西就可以编写几何库vectortransform或者distance。这些只是标准库中使用的许多非常常见的名称的示例。建议不要出于恐惧或对C ++不可或缺的一部分的命名空间功能的偏见而使用它们,这会适得其反。
Christian Rau

419

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

但是,您可以随时在(专用)*。cpp文件中使用using语句。


请注意,有些人不同意我这样说的“随意”-因为尽管usingcpp文件中的语句比标头中的语句更好(因为它不会影响包含您的标头文件的人),但他们认为它仍然没有(因为取决于代码,这可能会使类的实现更加难以维护)。这个C ++ Super-FAQ条目说,

using指令存在于旧版C ++代码中,并可以简化向名称空间的过渡,但是您可能不应该定期使用它,至少在新的C ++代码中不应该使用它。

FAQ提出了两种选择:

  • 使用声明:

    using std::cout; // a using-declaration lets you use cout without qualification
    cout << "Values:";
  • 只需输入std ::

    std::cout << "Values:";

1
当然,您也永远不应该假设全局cout的状态,以免有人使用std:cout << std :: hex并且事后未能通过std :: restore_cout_state。但这就是另外一个胖子。
莫兹

233

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

using namespace std;
using namespace boost;

许多Boost功能进入C ++ 0x标准,而Visual Studio 2010具有很多C ++ 0x功能,因此突然这些程序没有被编译。

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


14
这个。Boost和std有很多重叠-特别是从C ++ 11开始。
einpoklum '16

1
我曾经做过一次,并且以艰难的方式学到了教训。现在,我从不using在函数定义之外使用using namespace,也很少使用。
Ferruccio

210

简短版本:不要using在头文件中使用全局声明或指令。随时在实现文件中使用它们。这是赫伯·萨特Herb Sutter)安德烈·亚历山大Andrei Alexandrescu)C ++编码标准中对这个问题所要说的(强调一点是我的意思):

摘要

命名空间的使用是为了您的方便,而不是您要施加给其他人:切勿在#include指令之前编写using声明或using指令。

结论:在头文件中,请勿使用指令或声明来编写名称空间级别;而是显式地命名空间限定所有名称。(第二条规则是从第一条开始的,因为标头永远不知道其他标头#includes可能在它们之后出现。)

讨论区

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


4
此处仅是程序员的一种意见,但是尽管我100%同意该词using永远不应出现在标题中的说法,但我对using namespace xyz;在代码中放置任何地方的免费许可证并没有那么确信,尤其是如果xyzstd。我使用这种using std::vector;形式,因为这只会将名称空间中的单个元素拉入伪全局范围,因此导致发生碰撞的风险大大降低。
dgnuff '16

2
当然,@ Lightness Races在轨道上您有权发表自己的意见。如果尝试解释为什么您不同意此答案中给出的建议,本来会更有帮助。如果“使用”名称空间不好,那么理解名称空间的意义会特别有趣吗?为什么不只是用std_cout而不是std :: cout来命名……C ++ /命名空间的创建者在烦恼创建它们时一定已经有了一些想法。
nyholku

1
@nyholku:不需要-其他大多数答案都给出了与我相同的理由。也请不要犹豫,注意我在评论中附加的“ :)”!而且我没有说过命名空间不好。
Lightness Races in Orbit

是的,我注意到了:),但是IMO的大多数答案(违背了圣人的建议)都被误导了(不是我作了任何统计,现在的多数是这样)。如果您同意名称空间“不错”,那么如果您不同意此答案,那么您可能会说出您认为它们合适的位置?
nyholku

我禁不住觉得using namespace邪恶就像goto邪恶。两者都有有效的用途,但是在1000中有999次被错误地使用。所以,是的,using namespace在源代码中,您不会污染其他包含项的名称空间。但是它仍然无法保护您免受+ 调用(隐式Foo::)而产生的“乐趣”,突然间,代码中断(没有相关更改)仅仅是因为被添加到某处,这恰好是一个更好的选择比赛(因此现在被称为)using namespace Foousing namespace Barbaz(xyz)Bar::baz()
CharonX

122

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

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

这比显式限定(std::sinstd::cos...)更好,因为它更短并且具有使用用户定义的浮点类型的能力(通过依赖于参数的查找(ADL))。


9
对不起,但是我对此表示强烈反对。
Billy ONeal,2010年

4
@Billy:没有其他方法可以支持调用userlib :: cos(userlib :: superint)。每个功能都有用途。
Zan Lynx

17
@赞:当然有。using std::cos;using std::sin等等问题是,虽然任何精心设计userlib都将有自己sincos自己的命名空间内为好,所以这真的帮不了你。(除非using namespace userlib此模板之前有一个,而且与using namespace std- 一样糟糕,并且范围不受限制。)此外,我见过的唯一这样的函数是swap,在这种情况下,我建议只创建一个模板专业化std::swap并避免整个问题。
Billy ONeal

11
@BillyONeal:template<typename T> void swap(MyContainer<T>&, MyContainer<T>&)((没有功能模板部分专业化(FTPS),因此有时您需要求助于重载。)
sbi 2012年

38
@BillyONeal:您的评论(被赞成7次!)是错误的-您所描述的情况正是 ADL旨在解决的问题。简而言之,如果x具有一个或多个“关联名称空间”(例如,如果在中定义namespace userlib),则任何看起来像的函数调用都cos(x)另外查找这些名称空间- 无需任何using namespace userlib;事先准备。Zan Lynx是正确的(C ++名称查找是
byzantine

97

请勿在全球范围内使用

仅在全局使用时才被视为“不良” 。因为:

  • 您使正在编程的名称空间混乱。
  • 当您使用many时,读者将很难查看特定标识符的来源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 members
    swap(a.value_, b.value_); // `std::stwap(int, int)`
    swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
}

这具有以下魔力:

  • 编译器将选择std::swapfor value_,即void std::swap(int, int)
  • 如果void swap(Child&, Child&)实现了重载,编译器将选择它。
  • 如果没有该重载,编译器将使用void std::swap(Child&,Child&)并尽最大努力交换它们。

使用C ++ 11,就没有理由再使用此模式了。std::swap已更改的实现,以找到潜在的过载并选择它。


5
“更改了std :: swap的实现以找到潜在的重载并选择它。” - 什么?你确定吗?尽管swap在C ++ 11中首先提供自定义确实不那么重要,因为它std::swap本身更灵活(使用移动语义)。但是std::swap自动选择您自己的自定义交换,这对我来说绝对是新手(我真的不相信)。
Christian Rau

@ChristianRau我认为是的。我在某处这样读。我们总是可以问霍华德,他应该知道。我现在正在挖掘挖掘 ……
towi

14
即使在交换情况下,更清晰(也是更常见)的惯用语是写using std::swap;而不是using namespace std;。更具体的习惯用法具有较少的副作用,因此使代码更易于维护。
Adrian McCarthy 2014年

11
最后一句话是错误的。在C ++ 11中,“ 标准交换两步”被正式加为正确的调用方式swap,并且标准中的其他各个地方也更改为这样称呼swap(如上所述,注意NB using std::swap是正确的方式,而不是using namespace std)。但是,std::swap它本身并没有改变以找到其他东西swap并使用它。如果std::swap被调用,std::swap则被使用。
乔纳森·韦基利

3
using std::swap不过,最好只在本地键入内容,以减少本地名称空间,同时创建自文档代码,这是更明智的选择。您很少会有意整STD命名空间,所以刚走出挑选出你感兴趣的部分。
伦丁

79

如果导入正确的头文件,你突然有名字,如hexleftpluscount在全局范围内。如果您不知道其中std::包含这些名称,这可能会令人惊讶。如果您还尝试在本地使用这些名称,则可能导致相当混乱。

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


12
+1更不用说了distance。在实际可行的情况下,我仍然会选择不合格的名称,因为这样可以提高我的可读性。另外,我认为我们通常不具备口头表达能力,并且愿意花时间解决可能的歧义这一事实,这意味着能够理解没有资格的人在谈论什么,并将其应用于信息来源具有价值。代码意味着它的结构方式使得即使没有资格也可以清楚地了解所有内容。
干杯和健康。-阿尔夫

公平地说,如果您不包含,则其中的大多数就不会包含在内<iomanip>。不过,好点。
einpoklum '16

48

另一个原因是惊喜。

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


25
你在开玩笑吗?我真的不能告诉。如果不是这样的话,除非您不信任该代码,否则我个人会认为这是正常的“提示”,因为否则,这将是一次超越主要代码的气味,IMO。...而且,如果您不信任代码,那么为什么要首先使用它?请注意,我并不是在说“ TRUST EVEERY”!但是,如果要处理来自GitHub的某个知名库或其他东西,这似乎也有些牵强。
布伦特·里滕豪斯

28
@BrentRittenhouse cout是一个不好的例子,因为每个人都认可它。但是想象一下future在金融应用程序中。是在指定日期买卖某物的合同吗?不,不是。如果代码表明std::future您不会那么容易困惑。
詹姆斯·霍利斯

2
@BrentRittenhouse也许是一个不好的例子,至少有四个不同的库都有cout。可能是“它是标准库吗?libstdc ++?stl还是其他?” 不,并不是每个人都知道std :: cout,至少在本质上,我们收到的7名新员工中有6名没有。因为教育课程没有在教育中使用。我必须赶走printfs。或debugs()-来自Qt。
斯威夫特-星期五派

1
真?在第一本书的第一章的第一个示例中,这几乎就是很多关于C ++的书籍,如果有的话(带有插入运算符的用法)是某些新的身体唯一的 C ++。
mckenzm

@mckenzm为了减少混乱,我可以将其放在书或讲义中,但不能放在代码中
Martin Beckett

45

有经验的程序员会使用解决问题的方法,避免产生新的问题,并且出于这个确切原因,他们避免使用头文件级的using指令。

经验丰富的程序员还尝试避免在源文件中使用完全限定的名称。造成这种情况的一个次要原因是,除非有充分的理由,否则在编写更少的代码时就编写更多的代码并不是很优雅。这样做的主要原因是要关闭基于参数的查找(ADL)。

这些好的理由是什么?有时,程序员明确希望关闭ADL,而其他时候则想消除歧义。

所以以下是可以的:

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

43

我同意不应在全球范围内使用它,但在本地使用它(如在中)并不是那么邪恶namespace。这是“ C ++编程语言”中的示例:

namespace My_lib {

    using namespace His_lib; // Everything from His_lib
    using namespace Her_lib; // Everything from Her_lib

    using His_lib::String; // Resolve potential clash in favor of His_lib
    using Her_lib::Vector; // Resolve potential clash in favor of Her_lib

}

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

在那里显式声明的名称(包括通过using-declaration等声明的His_lib::String名称)优先于using伪指令(using namespace Her_lib)在另一个作用域中可访问的名称。


29

我也认为这是不好的做法。为什么?只是有一天,我认为名称空间的功能是划分内容,所以我不应该将所有内容都放入一个全局包中而破坏它。

但是,如果我经常使用'cout'和'cin',我会using std::cout; using std::cin;在.cpp文件中写:(在头文件中不会随传播#include)。我认为没有人会理智地为一条小溪cout或小溪命名cin。;)


7
那是一个本地using 声明,与using 指令完全不同。
2012年

25

很高兴看到代码并知道它的作用。如果我std::cout知道,那就是图书馆的工作cout流程std。如果我看到了,cout那我就不知道了。它可能是库的coutstd。或int cout = 0;同一功能可能高十行。或在该文件中static命名的变量cout。可能是任何东西。

现在以一百万行的代码库为例,这不是特别大,并且您正在寻找一个错误,这意味着您知道一百万行中有一行没有执行应做的工作。cout << 1;可以读取一个static intnamed cout,将其向左移动一位,然后丢弃结果。寻找错误,我必须检查一下。你能看到我真的很喜欢看std::cout吗?

如果您是一名教师,而不必为维持生活而编写和维护任何代码,那么其中的一件事情似乎就是一个好主意。我喜欢在以下地方查看代码:(1)我知道它的作用;(2)我相信编写它的人知道它的作用。


4
您如何知道“ std :: cout << 1”没有在std名称空间中读取名为cout的静态int并将其移位一个并丢弃结果?另外你怎么知道“ <<”的作用;)??? ...似乎此答案不是避免“使用”的好数据点。
nyholku 2015年

4
如果有人将std :: cout重新定义为整数,那么您的问题不是技术问题,而是社会问题-有人为您提供了它。(而且您可能还应该检查所有标头中是否包含#define true false等内容)
Jeremy Friesner '16

2
当我看到cout时,我总是知道它是std :: cout。如果我错了,那是写此代码的人的问题,而不是我:)
Tien Do

22

一切都与管理复杂性有关。使用名称空间将使您不需要的东西变得多余,从而可能使调试变得更加困难(我说可能)。在所有地方使用std ::很难阅读(更多文字等等)。

课程的马匹-管理您的复杂性,如何才能最好地做到最好。


18

考虑

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


// someoneElses.cpp/h
#include "myHeader.h"

class stringstream {  // Uh oh
};

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

这并不可怕,但是您可以通过不在头文件或全局名称空间中使用它来避免麻烦。在非常有限的范围内执行此操作可能是正确的,但是键入附加的五个字符来阐明函数的来源从来没有问题。


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

  2. 如果您仅使用cout,没有人会感到困惑。但是,当您有很多名称空间在四处游荡,并且看到此类时,您不确定它的作用是什么,将名称空间显式用作某种注释。乍看之下,“哦,这是文件系统操作”或“正在做网络工作”。


17

同时使用多个名称空间显然是灾难的根源,但在我看来std,仅使用名称空间和仅使用名称空间std并不是一件大事,因为重新定义只能由您自己的代码进行...

因此,只需将它们用作“ int”或“ class”之类的保留名称即可。

人们应该不再对它如此肛交。你的老师一直都是对的。只需使用一个名称空间;这就是首先使用名称空间的全部意义。您不应同时使用多个。除非是你自己的。因此,再次定义将不会发生。


创建碰撞并不难-短字符串喜欢minendless出现在std::命名空间。但是,更多的内容现在std::已经包含成千上万个符号,对于读者了解他们可能不知道的新符号来自何处很有用。
汤姆·斯威利

之所以存在std名称空间,是因为您,您的同事或编写您使用的中间件的人并不总是明智的将函数放入名称空间中。因此,您可以导入所有std ::和其他任何东西,同时仍要调用std :: min与其他人的旧版:: min()发生冲突,而该冲突要早于std :: min进入之。
艾肯鼓

14

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

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

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

并在实施中:

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

相对于:

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

要么:

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

只是一个小小的评论,尽管typedef很有用,但我会考虑制作一个代表Lines的类,而不是使用typedef。
艾尔·索尔尼克

14

一个具体的例子可以澄清这个问题。想象一下,您有两个库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。这是不希望的,但是幸运的是,这非常容易(只需foo::a编译器标记为歧义的所有调用之前添加)。

但是,想象一下另一种情况,其中bar改为改为如下所示:

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

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

使用命名空间时,您会冒这样的风险,这就是为什么人们对使用命名空间感到不舒服的原因。一个名称空间中的事物越多,发生冲突的风险就越大,因此std与其他名称空间相比,使用名称空间(由于该名称空间中的事物数量),人们可能会更加不自在。

最终,这是可写性与可靠性/可维护性之间的权衡。可读性也可能是其中的原因,但我可以看到无论哪种方式都存在争议。通常,我会说可靠性和可维护性更为重要,但是在这种情况下,您将不断支付可写性成本,以实现相当罕见的可靠性/可维护性影响。“最佳”权衡将取决于您的项目和优先级。


第二种情况对我来说很重要。再次没有名称空间。不能在引擎盖下发现如此细微的功能变化。
safe_malloc

13

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

namespace Mylib{
    template<class T> class Stack{ /* ... */ };
    // ...
}

namespace Yourlib{
    class Stack{ /* ... */ };
    // ...
}

void f(int max) {
    Mylib::Stack<int> s1(max); // Use my stack
    Yourlib::Stack    s2(max); // Use your stack
    // ...
}

重复命名空间名称可能会使读者和写作者分心。因此,可以声明来自特定名称空间的名称无需显式限定即可使用。例如:

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

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

资料来源: Bjarne Stroustrup撰写的C ++编程语言概述


4
非常有趣的是,这个答案是基于Bjarne Stroustrup从未获得过其他人的指导而获得的-2 ...男孩Bjarne在向C ++引入此功能时一定是一个贫穷而又没有经验的程序员
nyholku

@nyholku:看这个
sbi 2015年

10

using namespace std由于count的歧义性而引发编译错误的示例,它也是算法库中的函数。

#include <iostream>

using namespace std;

int count = 1;
int main() {
    cout << count << endl;
}

2
::count - 问题解决了。通常,从std命名空间获取的东西要比从其他地方获取的东西多,因此,保持使用using命名空间指令可能会节省您的键入时间。
PSkocik

真正的问题是C ++仍然具有较少名称空间的全局变量。这个,以及“ this”在方法中隐含的事实,导致了很多错误和问题,即使使用正确的“ count”变量,我什至无法计算它们。;)
艾肯鼓

9

它不会使您的软件或项目性能变差。在源代码的开头包含名称空间也不错。对列入using namespace std指令将根据您的需要和正在开发的软件或项目的方式各不相同。

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

本页所述

使用命名空间std的语句通常被认为是不好的做法。该语句的替代方法是每次我们声明类型时,都使用范围操作符(::)指定标识符所属的名称空间。

并查看此意见

当您大量使用命名空间并确定不会冲突时,在源文件中使用“使用命名空间std”是没有问题的。

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

本页所述

尽管该语句使我们不必键入std ::,但每当我们希望访问std名称空间中定义的类或类型时,它都会将std名称空间的整体导入到程序的当前名称空间中。让我们举几个例子来理解为什么这可能不是一件好事

...

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

...

请注意,cout指向哪个库有歧义?编译器可能会检测到此情况,而不编译程序。在最坏的情况下,程序可能仍会编译但调用错误的函数,因为我们从未指定标识符所属的名称空间。


8

我认为这并非在所有情况下都是不好的做法,但是在使用它时需要小心。如果要编写库,则可能应该将作用域解析运算符与名称空间一起使用,以防止您的库与其他库发生冲突。对于应用程序级代码,我认为没有任何问题。


7

“为什么要'使用命名空间std;' 被认为是C ++中的不良做法?”

我反过来说:为什么为什么要输入五个多余的字符,有些人觉得很麻烦?

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


19
这不仅仅是5个额外的字符。每当您在标准库中引用任何对象类型时,其5个额外的字符。如果您经常使用标准库,那么经常会这样做。因此,在一个像样的程序中,它实际上是成千上万个额外的字符。大概在语言中添加了“ using”指令,以便可以使用...
Jeremy Friesner 2013年

5
它不是每次5个多余的字符,而是5个字符,可能只需单击几下鼠标即可下拉菜单并在您选择的编辑器中执行“查找和替换”。
DaveWalley 2014年

1
可读性。cout << hex << setw(4) << i << endl;std::cout << std::hex << std::setw(4) << i << std::endl;
oz1cz

16
更糟糕的是:std::map<std::string,std::pair<std::string,std::string>>与相比,这太可怕了map<string,pair<string,string>>
oz1cz 2014年

4
好的做法是无论如何都要对STL容器进行typedef,这样std ::真的没关系。C ++ 11为我们带来了auto关键字,当使用迭代器时,它使事情变得更加轻松。
juzzlin '16

7

我同意其他人的看法-它要求名称冲突,含糊不清,然后事实是它不够明确。虽然可以看到的使用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”的概念。

实际上,我仍然非常偏向C和偏向C ++。保留细节,我所做的大部分工作都更适合C(但这是一个很好的练习,也是使自己成为一个好方法a。学习另一种语言和b。尽量不要偏向于对象/类/等,这可能更好地表述了更少的胸襟,更少的傲慢和更多的接受。)但是,什么有用的就是一些已经建议:我确实不使用列表(这是很普通的,不是吗?),和排序(同样的事情)来命名的两个愿引起名称冲突,如果我这样做using namespace std;,所以为此,我更希望在控制方面具有特定性,并且知道如果我打算将其用作标准用途,则必须指定它。简单地说:不允许假设。

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


1
扩展std名称空间是未定义的行为,因此永远不要执行。
tambre

7

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

例如,如果我输入,using namespace std;然后using namespace otherlib;只输入cout(恰好在两者中),而不是std::cout(或'otherlib::cout'),那么您可能使用了错误的代码并得到了错误。使用起来更加有效std::cout


6

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


6

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


6

这是一种不良做法,通常称为全局名称空间污染。当多个名称空间具有相同的带有签名的函数名称时,可能会出现问题,然后编译器决定要调用哪个名称将是模棱两可的,而在使用函数调用指定名称空间时,可以避免所有这些std::cout。希望这可以帮助。:)


5

为了回答您的问题,我实际上是这样看的:很多程序员(不是全部)都调用命名空间std。因此,应该养成不要使用与名称空间std中存在冲突或使用相同名称的事物的习惯。这是很重要的,但与严格说来可能出现的连贯的单词和假名相比,并没有那么多。

我的意思是真的……说“不依赖于此”就意味着您要设置为不依赖于此。借用代码段并不断对其进行修复时,您将不断遇到问题。只需将用户定义和借用的内容限制在应有的范围内,并尽量避免与全局变量一起使用(诚实地说,全局变量几乎永远是“现在编译,以后再理”的最后选择)。确实,我认为这是您老师的不明智建议,因为使用std既可用于“ cout”和“ std :: cout”,但不使用std只能用于“ std :: cout”。您不会总是很幸运地编写所有自己的代码。

注意:在真正了解编译器的工作方式之前,不要过多地关注效率问题。有了一点编码的经验,您就无需了解太多关于它们的知识,而是可以了解他们能够将好的代码归纳为简单的东西的程度。每一件事都像您用C语言编写了整个代码一样简单。好的代码仅在需要时才复杂。


鉴于有多少人似乎不知道有用的标准库功能(<algorithm>例如,从中重新发明了东西),想像一下同一个人可以可靠地避免使用这些标识符似乎有些困难。查看您自己的代码,告诉我您永远不会有一个名为的变量或函数count。或者distance,或者logdestroylaunchvisitbetasamplemessagesclamperasecopymodulusleft,等且不说所有标识符尚未std将打破你的代码时,C ++ 35出来...
托比斯佩特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.