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


其他人告诉我写作 using namespace std 在代码中是错误的,我应该使用 std::cout 和 std::cin 直接代替。

为什么是 using namespace std 被认为是不好的做法?它是否效率低或是否有风险声明模糊变量(变量与函数中的函数同名) std 命名空间)?它会影响性能吗?


2069
2017-09-21 03:08


起源


别忘了你能做到:“使用std :: cout;”这意味着您不必键入std :: cout,但不要同时引入整个std命名空间。 - Bill
@a付费的书呆子 google-styleguide.googlecode.com/svn/trunk/... 链接不再起作用。看起来像新链接 google.github.io/styleguide/cppguide.html#Other_C++_Features - MCG
在头文件中的文件范围内使用'using namespace std'特别糟糕。在所有包含之后在文件范围内的源文件(* .cpp)中使用它并不是那么糟糕,因为它的效果仅限于单个翻译单元。在函数或类中使用它的问题更少,因为它的作用仅限于函数或类范围。 - sh-
我不鼓励使用using指令但是对于特定的命名空间 std::literals::chrono_literals, Poco::Data:Keywords,Poco::Units 以及处理文字或可读性技巧的东西。每当它在头文件或实现文件中。在我猜的函数范围中可能没问题,但除了文字和东西外,它没用。 - Ludovic Zenohate Lagouardette
@Jon:特别是与命名空间std无关。我的重点是“在头文件中的文件范围”。把它作为建议:不要在头文件中的文件范围使用“using namespace”(std或其他)。可以在实现文件中使用它。抱歉模棱两可。 - sh-


答案:


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

using namespace foo;
using namespace bar;

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

如果你曾经使用过 foo::Blah() 和 bar::Quux(),然后介绍 foo::Quux() 本来不是一件事。


1751
2017-09-21 03:13



我一直很喜欢Python的“import big_honkin_name as bhn”,所以你可以使用“bhn.something”而不是“big_honkin_name.something” - 真正减少输入。 C ++有类似的东西吗? - paxdiablo
@Pax namespace io = boost :: filesystem; - AraK
我认为这是夸大其词,说它是“一些努力修复”。您将没有新foo :: Quux的实例,因此只需使用bar :: Quux消除所有当前用途的歧义。 - MattyT
是否有任何明智的人创建了一个库,其类型的不合格名称与std类型相冲突? - erikkallen
@TomA:问题所在 #define 是它不限制自己的命名空间,但践踏整个代码库。命名空间别名就是您想要的。 - sbi


我同意一切 格雷格写道,但我想补充一下: 它甚至可能比格雷格说的更糟糕!

Library Foo 2.0可以引入一个函数, Quux(),这是一些明确更好的匹配你的一些电话 Quux() 比 bar::Quux() 你的代码要求多年。然后你的 代码仍然编译但是 它默默地调用错误的函数 上帝知道什么。事情就像事情一样糟糕。

请记住 std namespace有很多标识符,其中很多都是 非常 常见的(想想 listsortstringiterator等等,也很可能出现在其他代码中。

如果你认为这不太可能:有 一个问题 这里发生了Stack Overflow,其中恰好发生了这种情况(由于省略了错误的函数) std:: 在我给出这个答案后大约半年。 这里 是这个问题的另一个最近的例子。 所以这是一个真正的问题。


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

十年来,该项目增长了数百万行代码。由于这些讨论一次又一次地出现,我曾经很好奇(允许的)功能范围 using实际上是在项目中使用的。我找到它的来源,只找到了一两个地方使用它。对我而言,这表明,一旦尝试过,开发人员就找不到了 std:: 即使在允许使用指令的情况下,即使每100kLoC使用指令也足够痛苦。


底线:明确地为所有内容添加前缀不会造成任何伤害,很少习惯,并具有客观优势。特别是,它使编码器和人类读者更容易理解代码 - 这应该是编写代码时的主要目标。


1152
2017-09-21 09:26



它会严重损害您可以在一行中打包的代码密度。你最终以一种非常冗长的方式编写代码;这降低了可读性。就个人而言,我认为较短(但不是太短)的代码往往更具可读性(因为阅读的东西较少,而且分散的东西较少)。 - Lie Ryan
猜猜你错过了C ++有标准之前的旧时代 string 上课,看似每个图书馆都有自己的。告诉你:我们将继续编写代码 std::,你可以运行我们的代码 grep -v std:: | vim 当你浏览它时。或者你可以教你的编辑 std:: 是一个与背景颜色相同的关键字。无论什么有效。 - Mike DeSimone
我不认为 std:: 是有害的。它带有非常重要的信息(即“无论是标准库的一部分”,它仍然是一个非常短而紧凑的前缀。大多数时候,它根本不是问题。有时,你有几行代码你需要在哪里引用特定的符号 std 命名空间很多,然后是 using 该特定范围内的陈述很好地解决了这个问题。但在一般情况下,它不是噪音,它传达了有价值的信息 此外 消除歧义。 - jalf
每当我看到 std::,我知道它会来自 std:: 无需考虑它。如果我明白了 string 要么 list 要么 map 我自己也有点疑惑。 - Mateen Ulhaq
@LieRyan然后祝你好好写一个几何图书馆而不用命名 vector, transform 要么 distance。这些只是其中的一个例子 许多很常见的名字 用于标准库。建议不要出于对C ++不可或缺的命名空间特性的恐惧或偏见而使用它们会产生相反的效果。 - Christian Rau


我认为把它放在你的类的头文件中是很糟糕的:因为那样你就会强迫任何想要使用你的类(通过包含你的头文件)也可以'使用'(即看到所有内容)这些其他命名空间。

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


请注意,有些人不同意我的说法“感觉自由”,因为尽管cpp文件中的using语句是 更好 而不是在标题中(因为它不会影响包含您的头文件的人),他们认为它仍然没有  (因为根据代码,它可能使类的实现更难维护)。 这个FAQ主题 说,

using-directive存在于遗留C ++代码中,并且可以简化向命名空间的转换,但您可能不应该定期使用它,至少不应该在新的C ++代码中使用它。

它提出了两种选择:

  • 使用声明:

    using std::cout; // a using-declaration lets you use cout without qualification
    cout << "Values:";
    
  • 克服它,只需键入std ::

    std::cout << "Values:";
    

309
2017-09-21 03:22



将它放在.cpp文件中比在标题中放置要简单,但问题在于可维护性问题保持不变。当你使用的代码/库/ C ++标准被修改时,它冒着两个具有相同名称的函数会发生冲突的风险。 - Étienne
你有没有试过这个?因为using namespace指令不会转移到另一个文件中。 (GCC 4.8+) - zackery.fix
@ zackery.fix,“Apple LLVM版本7.0.2(clang-700.1.81)”传播 using namespace std; 从头文件到源文件,我验证了GCC没有。所以至少在头文件中使用“using”指令是有风险的。 - Franklin Yu
是的..我不使用LLVM或clang,这不是一种标准的方法。 - zackery.fix
你真的不应该告诉人们在.cpp文件中使用命名空间std“感到自由”。 @Étienne是对的。 - einpoklum


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

using namespace std;
using namespace boost;

许多 促进 功能进入C ++ 0x标准,Visual Studio 2010有很多C ++ 0x功能,所以突然这些程序没有编译。

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


197
2017-10-28 17:37



这个。 Boost和std有一个 批量 重叠 - 尤其是从C ++ 11开始。 - einpoklum
我这样做了一次并且艰难地吸取了教训。现在我从来没用过 using 在函数定义之外,很少使用 using namespace 一点都不 - Ferruccio


简短版本:不要在头文件中使用全局使用声明或指令。随意在实现文件中使用它们。以下是Herb Sutter和Andrei Alexandrescu对此问题的评论 C ++编码标准 (重点强调是我的):

概要

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

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

讨论

简而言之:在#include指令之后,你可以而且应该在你的实现文件中使用声明和指令来使用命名空间,并且感觉良好。 尽管反复断言,使用声明和指令的命名空间并不是邪恶的,并且它们不会破坏命名空间的目的。相反,它们是使命名空间可用的原因


159
2017-11-03 20:00



遗憾的是,这个理智的指导被错误的答案所掩盖。 - nyholku
这里只是一个程序员的意见,但我同意100%的声明这个词 using 永远不应该出现在标题中,我不相信放置的免费许可证 using namespace xyz; 代码中的任何位置,尤其是 xyz 是 std。我用 using std::vector; 形式,因为它只将一个元素从命名空间中拉入伪全局范围,因此导致碰撞风险大大降低。 - dgnuff
在轨道上的@Lightness Races你当然有资格获得你的意见。如果有一些尝试解释为什么你不同意这个答案给出的建议会更有帮助。特别是如果“使用”它们是坏的,理解名称空间的重点是有趣的吗?为什么不直接命名std_cout而不是std :: cout ... C ++ / namespace的创建者在创建它们时必须有一些想法。 - nyholku
@nyholku:没有必要 - 其他大多数答案给出的理由与我相同。另外请不要犹豫,注意我添加到评论中的“:)”!而且我没有说名称空间是坏的。 - Lightness Races in Orbit
是的,我注意到了:)但IMO的大部分回答(违背这个圣人的建议)都是错误的(并不是我做了任何统计现在的大多数)。如果您同意命名空间“不错”,那么如果您不同意这个答案,您可能会说出您认为合适的位置? - nyholku


不应该在全局范围内使用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...) 因为它更短并且能够使用用户定义的浮点类型(通过Argument Dependent Lookup)。


103
2017-09-21 15:47



对不起,我强烈不同意这一点。 - Billy ONeal
@Billy:没有其他方法可以支持调用userlib :: cos(userlib :: superint)。每个功能都有用。 - Zan Lynx
@Zan:当然有。 using std::cos; , using std::sin等等。问题是任何精心设计的 userlib 会有他们的 sin 和 cos 在他们自己的命名空间内,所以这对你没有帮助。 (除非有一个 using namespace userlib 在这个模板之前和那个一样糟糕 using namespace std  - 并且范围不受限制。)此外,我所见过的唯一这样的功能是 swap,在这种情况下,我建议只创建一个模板专业化 std::swap 并避免整个问题。 - Billy ONeal
@BillyONeal: template<typename T> void swap(MyContainer<T>&, MyContainer<T>&) (没有功能模板部分特化(FTPS),所以有时你需要求助于重载。 - sbi
@BillyONeal:你的(7次投票!)评论错了 - 你描述的情况是 究竟 ADL旨在涵盖的内容。简而言之,如果 x 有一个或多个“关联的命名空间”(例如,如果它是在...中定义的) namespace userlib)然后任何看起来像的函数调用 cos(x) 将 另外 查看这些名称空间 - 无 任何 using namespace userlib; 事先是必要的。 Zan Lynx是对的(并且C ++名称查找是拜占庭式的...) - j_random_hacker


不要在全球范围内使用它

只有当它被认为是“坏”时 全球使用。因为:

  • 你混乱了编程的命名空间。
  • 当您使用多个标识符时,读者将很难看到特定标识符的来源 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::swap 对于 value_,即 void std::swap(int, int)
  • 如果你有超负荷 void swap(Child&, Child&) 实现编译器会选择它。
  • 如果你这样做  有编译器将使用的重载 void std::swap(Child&,Child&) 并尝试最好地交换这些。

使用C ++ 11,没有理由再使用这种模式。实施 std::swap 被改为发现潜在的超载并选择它。


80
2018-01-18 09:34



“std :: swap的实现已经改变,以找到潜在的超载并选择它。” - 什么?你确定吗?虽然提供定制是真的 swap 首先,在C ++ 11中不再那么重要,因为 std::swap 本身更灵活(使用移动语义)。但 std::swap 自动选择你自己的自定义交换,这对我来说是绝对新的(我真的不相信)。 - Christian Rau
@ChristianRau我想是的,是的。我在某处读到了这个。我们总能问 霍华德他应该知道。我是 挖掘 和 挖掘 现在... - towi
即使在交换案例中,更清晰(更幸运的是更常见)的习惯是写 using std::swap; 而不是 using namespace std;。更具体的习语具有更少的副作用,因此使代码更易于维护。 - Adrian McCarthy
最后一句话是错的。在C ++ 11中 Std Swap两步 被正式祝福了 对 打电话的方式 swap,标准中的其他各个地方都被改为说他们打电话 swap 那样的(如上所述,N.B。 using std::swap 是正确的方式,而不是 using namespace std)。但 std::swap 本身就是重点 不 换了另外一些 swap 并使用它。如果 std::swap 然后被召唤 std::swap 得到了用。 - Jonathan Wakely
打字可能更明智 using std::swap 在本地,减少本地命名空间,同时创建自我文档代码。你很少对整个std命名空间感兴趣,所以只需挑出你感兴趣的部分。 - Lundin