“使用命名空间 std”有什么问题?
我听说 using namespace std;
是错误的,我应该直接使用 std::cout
和 std::cin
。
这是为什么呢?声明与 std
命名空间中的内容同名的变量是否存在风险?对性能有影响吗?
I have heard using namespace std;
is wrong, and that I should use std::cout
and std::cin
directly instead.
Why is this? Does it risk declaring variables that share the same name as something in the std
namespace? Are there performance implications?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
命名空间是一个命名范围。命名空间用于对相关声明进行分组并保持独立
项目分开。例如,两个单独开发的库可能使用相同的名称来引用不同的库
项,但用户仍然可以使用两者:
重复命名空间名称可能会分散读者和作者的注意力。因此,有可能
声明来自特定命名空间的名称无需明确限定即可使用。例如:
命名空间提供了一个强大的工具来管理不同的库和不同版本的代码。特别是,它们为程序员提供了如何显式引用非本地名称的替代方案。
资料来源:C++ 编程语言概述
作者:比亚尼·斯特鲁斯特鲁普
A namespace is a named scope. Namespaces are used to group related declarations and to keep separate
items separate. For example, two separately developed libraries may use the same name to refer to different
items, but a user can still use both:
Repeating a namespace name can be a distraction for both readers and writers. Consequently, it is possible
to state that names from a particular namespace are available without explicit qualification. For example:
Namespaces provide a powerful tool for the management of different libraries and of different versions of code. In particular, they offer the programmer alternatives of how explicit to make a reference to a nonlocal name.
Source: An Overview of the C++ Programming Language
by Bjarne Stroustrup
这是一个
using namespace std
因 count 的歧义而引发编译错误的示例,count 也是算法库中的一个函数。An example where
using namespace std
throws a compilation error because of the ambiguity of count, which is also a function in algorithm library.这是具体情况而定。我们希望最大限度地降低软件在其生命周期内的“总拥有成本”。声明“使用命名空间 std”会产生一些成本,但不使用它也会在易读性方面产生成本。
人们正确地指出,在使用它时,当标准库引入新的符号和定义时,您的代码将停止编译,并且您可能被迫重命名变量。然而,从长远来看,这可能是好的,因为如果您将关键字用于某些令人惊讶的目的,未来的维护人员会暂时感到困惑或分心。
例如,您想要有一个名为向量的模板,它不是其他人都知道的向量。而且 C++ 库中引入的新定义数量足够少,可能根本不会出现。进行此类更改是有成本的,但成本并不高,并且可以被不将 std 符号名称用于其他目的而获得的清晰度所抵消。
考虑到类、变量和函数的数量,在每个类、变量和函数上都声明
std::
可能会使您的代码混乱 50%,并使您更难以理解。可以在一屏代码上采用的算法或方法中的步骤现在需要来回滚动才能跟随。这是真实的成本。可以说,这可能成本并不高,但否认它存在的人要么缺乏经验,要么教条主义,或者根本就是错误的。我提供以下规则:
std
与所有其他库不同。它是每个人基本上都需要了解的一个库,在我看来最好将其视为语言的一部分。一般来说,即使其他库没有,使用命名空间 std
也是一个很好的例子。切勿通过将此
using
放入标头中来将决定强加给编译单元(.cpp 文件)的作者。 始终将决定推迟给编译单元作者。即使在一个已决定在所有地方使用using namespace std
的项目中,也可能会罚款一些最好作为该规则的例外处理的模块。尽管命名空间功能允许您拥有许多具有相同定义符号的模块,但这样做会很混乱。尽可能保持名称不同。即使不使用命名空间功能,如果您有一个名为
foo
的类,并且std
引入了一个名为foo
的类,从长远来看可能会更好无论如何都要重命名您的类。使用命名空间的另一种方法是通过给命名空间符号添加前缀来手动命名空间符号。我有两个已经使用了几十年的库,实际上都是从 C 库开始的,其中每个符号都以“AK”或“SCWin”为前缀。一般来说,这就像避免使用“using”结构,但你不写双冒号。
AK::foo()
改为AKFoo()
。它使代码密度增加了 5-10%,并且不再那么冗长,唯一的缺点是,如果您必须使用两个具有相同前缀的此类库,您将遇到大麻烦。请注意,X Window 库在这方面非常出色,只是他们忘记了使用一些 #define 来做到这一点:TRUE 和 FALSE 应该是 XTRUE 和 XFALSE,这与同样使用 TRUE 和 FALSE 的 Sybase 或 Oracle 建立了命名空间冲突具有不同的价值观! (对于数据库来说是 ASCII 0 和 1!)这一点的一个特殊优点是它可以无缝地应用于预处理器定义,而 C++using
/namespace
系统则不然。不要处理它们。这样做的一个好处是,它提供了从项目的一部分到最终成为图书馆的有机斜坡。在我的一个大型应用程序中,所有窗口类都以Win
为前缀,所有信号处理模块都以 Mod 为前缀,等等。这些被重用的机会很小,因此将每个组都变成一个库没有任何实际好处,但几秒钟内项目如何分解为子项目就变得显而易见。It's case by case. We want to minimize the "total cost of ownership" of the software over its lifespan. Stating "using namespace std" has some costs, but not using it also has a cost in legibility.
People correctly point out that when using it, when the standard library introduces new symbols and definitions, your code ceases to compile, and you may be forced to rename variables. And yet this is probably good long-term, since future maintainers will be momentarily confused or distracted if you're using a keyword for some surprising purpose.
You don't want to have a template called vector, say, which isn't the vector known by everyone else. And the number of new definitions thus introduced in the C++ library is small enough it may simply not come up. There is a cost to having to do this kind of change, but the cost is not high and is offset by the clarity gained by not using
std
symbol names for other purposes.Given the number of classes, variables, and functions, stating
std::
on every one might fluff up your code by 50% and make it harder to get your head around. An algorithm or step in a method that could be taken in on one screenful of code now requires scrolling back and forth to follow. This is a real cost. Arguably it may not be a high cost, but people who deny it even exists are inexperienced, dogmatic, or simply wrong.I'd offer the following rules:
std
is different from all other libraries. It is the one library everyone basically needs to know, and in my view is best thought of as part of the language. Generally speaking there is an excellent case forusing namespace std
even if there isn't for other libraries.Never force the decision onto the author of a compilation unit (a .cpp file) by putting this
using
in a header. Always defer the decision to the compilation unit author. Even in a project that has decided to useusing namespace std
everywhere may fine a few modules that are best handled as exceptions to that rule.Even though the namespace feature lets you have many modules with symbols defined the same, it's going to be confusing to do so. Keep the names different to the extent possible. Even if not using the namespace feature, if you have a class named
foo
andstd
introduces a class namedfoo
, it's probably better long-run to rename your class anyway.An alternative to using namespaces is to manually namespace symbols by prefixing them. I have two libraries I've used for decades, both starting as C libraries, actually, where every symbol is prefixed with "AK" or "SCWin". Generally speaking, this is like avoiding the "using" construct, but you don't write the twin colons.
AK::foo()
is insteadAKFoo()
. It makes code 5-10% denser and less verbose, and the only downside is that you'll be in big trouble if you have to use two such libraries that have the same prefixing. Note the X Window libraries are excellent in this regard, except they forgot to do so with a few #defines: TRUE and FALSE should have been XTRUE and XFALSE, and this set up a namespace clash with Sybase or Oracle that likewise used TRUE and FALSE with different values! (ASCII 0 and 1 in the case of the database!) One special advantage of this is that it applies seemlessly to preprocessor definitions, whereas the C++using
/namespace
system doesn't handle them. A nice benefit of this is that it gives an organic slope from being part of a project to eventually being a library. In a large application of mine, all window classes are prefixedWin
, all signal-processing modules Mod, and so on. There's little chance of any of these being reused so there's no practical benefit to making each group into a library, but it makes obvious in a few seconds how the project breaks into sub-projects.它不会使您的软件或项目性能变差。在源代码的开头包含命名空间也不错。
using namespace std
指令的包含情况根据您的需求以及开发软件或项目的方式而有所不同。命名空间 std
包含 C++ 标准函数和变量。当您经常使用 C++ 标准函数时,此命名空间非常有用。有些人说在源文件中包含
using namespace std
是一种不好的做法,因为您是从该命名空间调用所有函数和变量。当您想要定义一个与命名空间 std 中包含的另一个函数同名的新函数时,您将重载该函数,并且可能会因编译或执行而产生问题。它不会按您的预期编译或执行。It doesn't make your software or project performance worse. The inclusion of the namespace at the beginning of your source code isn't bad. The inclusion of the
using namespace std
instruction varies according to your needs and the way you are developing the software or project.The
namespace std
contains the C++ standard functions and variables. This namespace is useful when you often would use the C++ standard functions.Some people had said that is a bad practice to include the
using namespace std
in your source files because you're invoking from that namespace all the functions and variables. When you would like to define a new function with the same name as another function contained in thenamespace std
you would overload the function and it could produce problems due to compile or execute. It will not compile or executing as you expect.我同意其他人的观点——它要求名称冲突、含糊不清,但事实是它不太明确。虽然我可以看到
using
的用法,但我个人的偏好是限制它。我也会强烈考虑其他人指出的内容:如果您想找到一个可能是相当常见的名称的函数名称,但您只想在
std
命名空间中找到它(或相反 -您想要更改所有不在命名空间std
、命名空间X
中的调用,...),那么您建议如何执行此操作?您可以编写一个程序来完成此任务,但是花时间处理您的项目本身而不是编写一个程序来维护您的项目不是更好吗?
就我个人而言,我实际上并不介意
std::
前缀。我更喜欢它的外观而不是没有它。我不知道这是否是因为它是明确的并对我说“这不是我的代码......我正在使用标准库”或者它是否是其他东西,但我认为它看起来更好。这可能很奇怪,因为我最近才开始接触 C++(使用并仍在使用 C 和其他语言更长时间,而且 C 是我一直以来最喜欢的语言,就在汇编之上)。还有一件事,尽管它与上述内容和其他人指出的内容有些相关。虽然这可能是不好的做法,但我有时会为标准库版本保留
std::name
并为特定于程序的实现保留名称。是的,确实这可能会咬你一口,而且咬得很厉害,但这一切都归结为我从头开始这个项目,而且我是它唯一的程序员。示例:我重载std::string
并将其命名为string
。我有一些有用的补充。我这样做的部分原因是我的 C 和 Unix (+ Linux) 倾向于使用小写名称。除此之外,您还可以拥有命名空间别名。这是一个可能没有被提及的有用示例。我使用 C++11 标准,特别是 libstdc++。嗯,它没有完整的
std::regex
支持。当然,它可以编译,但它会抛出一个异常,就像程序员的错误一样。但缺乏落实。这就是我解决问题的方法。安装 Boost 的正则表达式,并将其链接。然后,我执行以下操作,以便当 libstdc++ 完全实现它时,我只需要删除此块并且代码保持不变:
我不会争论这是否是一个坏主意或不是。然而,我会争辩说,它使我的项目保持干净,同时使其具体:确实,我必须使用Boost,但是我使用它就像libstdc++ 最终会拥有它。是的,开始您自己的项目并从一开始就使用标准(...)对于帮助维护、开发以及与项目相关的一切都有很大帮助!
只是为了澄清一些事情:我实际上认为在 STL 故意且更具体地代替。对我来说,字符串是个例外(忽略第一个、上面或第二个,如果你必须的话,双关语),因为我不喜欢“字符串”的想法。
事实上,我还是非常偏向C,偏向C++。省去细节,我所做的大部分工作更适合 C(但这是一个很好的练习,也是让自己 a. 学习另一种语言 b. 尽量不要对对象/类/等有偏见的好方法,这可能是更好的表述思想不那么封闭,不那么傲慢,更容易接受。)。但有用的是一些人已经建议的:我确实使用列表(它相当通用,不是吗?)和排序(相同的东西)来命名两个,如果我要
using namespace std;
,因此为此目的,我更喜欢具体、可控,并且知道如果我打算将其作为标准用途,那么我将必须指定它。简而言之:不允许假设。至于让 Boost 的正则表达式成为 std 的一部分。我这样做是为了将来的集成 - 再次,我完全承认这是偏见 - 我不认为它像
boost::regex:: ...
那样丑陋。确实,这对我来说是另一回事。 C++ 中有很多东西在外观和方法上我还没有完全接受(另一个例子:可变参数模板与 var 参数[尽管我承认可变参数模板非常非常有用!])。即使那些我确实接受这一点的人也很困难,我仍然对他们有意见。I agree with others – it is asking for name clashes, ambiguities and then the fact is it is less explicit. While I can see the use of
using
, my personal preference is to limit it. I would also strongly consider what some others pointed out:If you want to find a function name that might be a fairly common name, but you only want to find it in the
std
namespace (or the reverse – you want to change all calls that are not in namespacestd
, namespaceX
, ...), then how do you propose to do this?You could write a program to do it, but wouldn't it be better to spend time working on your project itself rather than writing a program to maintain your project?
Personally, I actually don't mind the
std::
prefix. I like the look more than not having it. I don't know if that is because it is explicit and says to me "this isn't my code... I am using the standard library" or if it is something else, but I think it looks nicer. This might be odd given that I only recently got into C++ (used and still do C and other languages for much longer and C is my favourite language of all time, right above assembly).There is one other thing although it is somewhat related to the above and what others point out. While this might be bad practise, I sometimes reserve
std::name
for the standard library version and name for program-specific implementation. Yes, indeed this could bite you and bite you hard, but it all comes down to that I started this project from scratch, and I'm the only programmer for it. Example: I overloadstd::string
and call itstring
. I have helpful additions. I did it in part because of my C and Unix (+ Linux) tendency towards lower-case names.Besides that, you can have namespace aliases. Here is an example of where it is useful that might not have been referred to. I use the C++11 standard and specifically with libstdc++. Well, it doesn't have complete
std::regex
support. Sure, it compiles, but it throws an exception along the lines of it being an error on the programmer's end. But it is lack of implementation.So here's how I solved it. Install Boost's regex, and link it in. Then, I do the following so that when libstdc++ has it implemented entirely, I need only remove this block and the code remains the same:
I won't argue on whether that is a bad idea or not. I will however argue that it keeps it clean for my project and at the same time makes it specific: True, I have to use Boost, but I'm using it like the libstdc++ will eventually have it. Yes, starting your own project and starting with a standard (...) at the very beginning goes a very long way with helping maintenance, development and everything involved with the project!
Just to clarify something: I don't actually think it is a good idea to use a name of a class/whatever in the STL deliberately and more specifically in place of. The string is the exception (ignore the first, above, or second here, pun if you must) for me as I didn't like the idea of 'String'.
As it is, I am still very biased towards C and biased against C++. Sparing details, much of what I work on fits C more (but it was a good exercise and a good way to make myself a. learn another language and b. try not be less biased against object/classes/etc which is maybe better stated as less closed-minded, less arrogant, and more accepting.). But what is useful is what some already suggested: I do indeed use list (it is fairly generic, is it not ?), and sort (same thing) to name two that would cause a name clash if I were to do
using namespace std;
, and so to that end I prefer being specific, in control and knowing that if I intend it to be the standard use then I will have to specify it. Put simply: no assuming allowed.And as for making Boost's regex part of
std
. I do that for future integration and – again, I admit fully this is bias - I don't think it is as ugly asboost::regex:: ...
. Indeed, that is another thing for me. There are many things in C++ that I still have yet to come to fully accept in looks and methods (another example: variadic templates versus var arguments [though I admit variadic templates are very very useful!]). Even those that I do accept it was difficult, and I still have issues with them.根据我的经验,如果您有多个使用
cout
的库,但出于不同的目的,您可能会使用错误的cout
。例如,如果我输入
using namespace std;
和using namespace otherlib;
并仅键入cout
(两者恰好都存在) ,而不是std::cout
(或'otherlib::cout'
),您可能会使用错误的,并出现错误。使用std::cout
更加有效和高效。From my experiences, if you have multiple libraries that uses say,
cout
, but for a different purpose you may use the wrongcout
.For example, if I type in,
using namespace std;
andusing namespace otherlib;
and type justcout
(which happens to be in both), rather thanstd::cout
(or'otherlib::cout'
), you might use the wrong one, and get errors. It's much more effective and efficient to usestd::cout
.我不认为在所有情况下这都一定是不好的做法,但使用时需要小心。如果您正在编写一个库,您可能应该将范围解析运算符与名称空间一起使用,以防止您的库与其他库发生冲突。对于应用程序级别的代码,我没有发现任何问题。
I do not think it is necessarily bad practice under all conditions, but you need to be careful when you use it. If you're writing a library, you probably should use the scope resolution operators with the namespace to keep your library from butting heads with other libraries. For application level code, I don't see anything wrong with it.
对于不合格的导入标识符,您需要 grep 等外部搜索工具来查找声明标识符的位置。这使得推理程序正确性变得更加困难。
With unqualified imported identifiers you need external search tools like grep to find out where identifiers are declared. This makes reasoning about program correctness harder.
为什么要避免
using namespace std;
?原因 1:避免名称冲突。
由于 C++ 标准库很大且不断扩展,因此命名空间在 C++ 中用于减少名称冲突。当您使用“using namespace std;”时,您正在批量导入所有内容。
这就是为什么“使用命名空间 std;”永远不会出现在任何专业代码中。
原因2:编译失败。
因为它将std命名空间中定义的数百个东西(类、方法、常量、模板等)拉入全局命名空间。它不仅对写入“using namespace std”本身的文件这样做,而且对包含它的任何文件都递归地这样做。这很容易导致意外的 ODR 违规和难以调试的编译器/链接器错误。
示例:
当您在全局命名空间中声明函数“max”时,您将使用 std 命名空间。
由于您没有使用 cmath 标头,因此一切似乎都运行良好。
当其他人包含您的文件和 cmath 标头时,他们的代码会意外地无法构建,因为全局命名空间中有两个名为“max”的函数。
原因 3:将来可能不起作用。
更糟糕的是,您无法预测将来会对 std:: 命名空间进行哪些更改。这意味着如果新添加的符号与代码中的某些内容发生冲突,今天运行的代码可能会在以后停止运行。
原因 4:难以维护和调试。
使用命名空间 std; 会产生难以维护和调试的代码。这是因为某些方面的来源并不总是显而易见。如果开发人员使用术语“字符串”而没有更多解释,则他们可能指的是 std::string 类或唯一字符串类。
带有
命名空间std
的代码没有
命名空间
但是你可以使用if,
如果你想制作简短的教程或程序等,你可以使用它。
当您大量使用命名空间并且确信不会发生冲突时,在源文件中使用“using namespace std”没有问题。
Why should
using namespace std;
be avoided?Reason 1: To avoid name collision.
Because the C++ standard library is large and constantly expanding, namespaces in C++ are used to lessen name collisions. You are importing everything wholesale when you use "using namespace std;".
That's why "using namespace std;" will never appear in any professional code.
Reason 2: Failed compilation.
Because it pulls the hundreds of things (classes, methods, constants, templates, etc.) defined in the std namespace into the global namespace. And it does so, not just for the file that writes “using namespace std” itself, but also for any file that includes it, recursively. This can very easily lead to accidental ODR violations and hard-to-debug compiler/linker errors.
Example:
You use the std namespace when you declare the function "max" in the global namespace.
Since you aren't using the cmath header, everything appears to be working well.
When someone else includes your file and the cmath header, their code unexpectedly fails to build because there are two functions with the name "max" in the global namespace.
Reason 3: May not work in future.
Even worse, you can't predict what changes will be made to the std:: namespace in the future. This means that code that functions today might cease to function later if a newly added symbol clashes with something in your code.
Reason 4: Difficult to maintain and debug.
The use of namespace std; can produce difficult-to-maintain and difficult-to-debug code. This is due to the fact that it is not always obvious where certain aspects come from. A developer might be referring to the std::string class or a unique string class if they use the term "string" without more explanation.
Code with
namespace std
Without
namespace
But you can use if,
you can use that if want to make short tutorials or programs, etc.
no problem using "using namespace std" in your source file when you make heavy use of the namespace and know for sure that nothing will collide.
这通常称为全局名称空间污染。当多个命名空间具有相同的函数名和签名时,可能会出现问题,那么编译器决定调用哪一个命名空间将是不明确的,而当您使用像
这样的函数调用指定命名空间时,这一切都可以避免std::cout
。This is often known as global namespace pollution. Problems may occur when more than one namespace has the same function name with signature, then it will be ambiguous for the compiler to decide which one to call and this all can be avoided when you are specifying the namespace with your function call like
std::cout
.我反过来说:为什么有些人认为额外输入五个字符很麻烦?
例如,考虑编写一个数值软件。当
vector
为 1 时,为什么我会考虑通过将常规std::vector
缩减为vector
来污染我的全局命名空间问题领域最重要的概念是什么?I put it the other way around: Why is typing five extra characters considered cumbersome by some?
Consider e.g. writing a piece of numerical software. Why would I even consider polluting my global namespace by cutting general
std::vector<T>
down tovector<T>
whenvector
is one of the problem domain's most important concepts?考虑两个名为 Foo 和 Bar 的库:
一切正常,您可以从 Foo 调用
Blah()
并从 Bar 调用Quux()
,不会出现任何问题。但有一天,您升级到了 Foo 2.0 的新版本,它现在提供了一个名为Quux()
的函数。现在您遇到了冲突:Foo 2.0 和 Bar 都将Quux()
导入到您的全局命名空间中。这将需要一些努力来修复,特别是如果函数参数恰好匹配的话。如果您使用过
foo::Blah()
和bar::Quux()
,那么引入foo::Quux()
将会已经成为一个非事件。Consider two libraries called Foo and Bar:
Everything works fine, and you can call
Blah()
from Foo andQuux()
from Bar without problems. But one day you upgrade to a new version of Foo 2.0, which now offers a function calledQuux()
. Now you've got a conflict: Both Foo 2.0 and Bar importQuux()
into your global namespace. This is going to take some effort to fix, especially if the function parameters happen to match.If you had used
foo::Blah()
andbar::Quux()
, then the introduction offoo::Quux()
would have been a non-event.的更糟糕!
它可能会比 Greg写 Foo 2.0 可以引入一个函数
Quux()
,对于您对Quux()
的某些调用来说,它比bar::Quux( )
你的代码调用了很多年。然后你的代码仍然可以编译,但是它默默地调用了错误的函数并且做了天知道什么的事情。这就是事情所能达到的最糟糕的情况了。请记住,
std
命名空间有大量标识符,其中许多非常常见(例如list
、sort、
string
、iterator
等)也很可能出现在其他代码中。如果您认为这不太可能:Stack Overflow 上有一个问题,几乎正是发生了这种情况(错误的函数称为 due在我给出这个答案大约半年后,省略了
std::
前缀。 这里是此类问题的另一个最新示例。所以这是一个真正的问题。
这里还有一个数据点:很多很多年前,我也曾经发现必须在标准库中的所有内容前面加上
std::
前缀很烦人。然后我在一个项目中工作,该项目从一开始就决定禁止除函数作用域之外的using
指令和声明。你猜怎么着?我们大多数人花了几周的时间才习惯编写前缀,几周后我们大多数人甚至同意它实际上使代码更具可读性。这是有原因的:您喜欢较短还是较长的散文是主观的,但前缀客观地增加了代码的清晰度。不仅是编译器,还有您,发现更容易看到引用了哪个标识符。十年内,该项目增长到拥有数百万行代码。由于这些讨论一次又一次地出现,我曾经很好奇(允许的)函数范围
using
在项目中实际使用的频率。我查找了它的来源,只找到了一两打使用它的地方。对我来说,这表明,一旦尝试过,开发人员不会觉得std::
足以痛苦地使用 using 指令,即使每 100 kLoC 一次,即使在允许使用它的地方也是如此。底线:明确地为所有内容添加前缀不会造成任何损害,只需很少的时间即可习惯,并且具有客观的优势。特别是,它使代码更容易被编译器和人类读者解释——这可能应该是编写代码时的主要目标。
It can get worse than what Greg wrote!
Library Foo 2.0 could introduce a function,
Quux()
, that is an unambiguously better match for some of your calls toQuux()
than thebar::Quux()
your code called for years. Then your code still compiles, but it silently calls the wrong function and does god-knows-what. That's about as bad as things can get.Keep in mind that the
std
namespace has tons of identifiers, many of which are very common ones (thinklist
,sort
,string
,iterator
, etc.) which are very likely to appear in other code, too.If you consider this unlikely: There was a question asked here on Stack Overflow where pretty much exactly this happened (wrong function called due to omitted
std::
prefix) about half a year after I gave this answer. Here is another, more recent example of such a question.So this is a real problem.
Here's one more data point: Many, many years ago, I also used to find it annoying having to prefix everything from the standard library with
std::
. Then I worked in a project where it was decided at the start that bothusing
directives and declarations are banned except for function scopes. Guess what? It took most of us very few weeks to get used to writing the prefix, and after a few more weeks most of us even agreed that it actually made the code more readable. There's a reason for that: Whether you like shorter or longer prose is subjective, but the prefixes objectively add clarity to the code. Not only the compiler, but you, too, find it easier to see which identifier is referred to.In a decade, that project grew to have several million lines of code. Since these discussions come up again and again, I once was curious how often the (allowed) function-scope
using
actually was used in the project. I grep'd the sources for it and only found one or two dozen places where it was used. To me this indicates that, once tried, developers don't findstd::
painful enough to employ using directives even once every 100 kLoC even where it was allowed to be used.Bottom line: Explicitly prefixing everything doesn't do any harm, takes very little getting used to, and has objective advantages. In particular, it makes the code easier to interpret by the compiler and by human readers — and that should probably be the main goal when writing code.
将
using namespace
放入类的头文件中的问题在于,它迫使任何想要使用您的类(通过包含头文件)的人也“使用”(即查看其中的所有内容)那些其他命名空间。但是,您可以随意将 using 语句放入您的(私有)*.cpp 文件中。
请注意,有些人不同意我这样说“随意”——因为虽然 cpp 文件中的
using
语句比标头中的更好(因为它不不会影响包含您的头文件的人),他们认为这仍然不好(因为根据代码,它可能会使类的实现更难以维护)。 此 C++ 超级常见问题解答条目 说,常见问题解答建议了两种替代方案:
使用声明:
只需输入 std::
The problem with putting
using namespace
in the header files of your classes is that it forces anyone who wants to use your classes (by including your header files) to also be 'using' (i.e. seeing everything in) those other namespaces.However, you may feel free to put a using statement in your (private) *.cpp files.
Beware that some people disagree with my saying "feel free" like this -- because although a
using
statement in a cpp file is better than in a header (because it doesn't affect people who include your header file), they think it's still not good (because depending on the code it could make the implementation of the class more difficult to maintain). This C++ Super-FAQ entry says,The FAQ suggests two alternatives:
A using-declaration:
Just typing std::
我最近遇到了有关 Visual Studio 2010 的投诉。事实证明,几乎所有源文件都有这两行:
很多 Boost 功能正在纳入 C++0x 标准,而 Visual Studio 2010 有很多 C++0x 功能,因此突然间这些程序无法编译。
因此,避免
using namespace X;
是一种面向未来的形式,一种确保对正在使用的库和/或头文件的更改不会破坏程序的方法。I recently ran into a complaint about Visual Studio 2010. It turned out that pretty much all the source files had these two lines:
A lot of Boost features are going into the C++0x standard, and Visual Studio 2010 has a lot of C++0x features, so suddenly these programs were not compiling.
Therefore, avoiding
using namespace X;
is a form of future-proofing, a way of making sure a change to the libraries and/or header files in use is not going to break a program.简短版本:不要在头文件中使用全局
using
声明或指令。请随意在实现文件中使用它们。以下是 Herb Sutter 和 Andrei Alexandrescu 必须在 C++ 编码标准(粗体表示强调是我的):Short version: don't use global
using
declarations or directives in header files. Feel free to use them in implementation files. Here's what Herb Sutter and Andrei Alexandrescu have to say about this issue in C++ Coding Standards (bolding for emphasis is mine):不应在全局范围内使用
using
指令,尤其是在标头中。然而,在某些情况下,即使在头文件中也是合适的:这比显式限定(std::sin、std::cos...)要好,因为它更短并且能够使用用户定义的浮点类型(通过 参数相关查找< /a>(ADL))。
One shouldn't use the
using
directive at the global scope, especially in headers. However, there are situations where it is appropriate even in a header file:This is better than explicit qualification (
std::sin
,std::cos
...), because it is shorter and has the ability to work with user defined floating point types (via argument-dependent lookup (ADL)).不要在全球范围内使用它
只有在全球范围内使用时才被认为是“坏”的。因为:
using namespace xyz;
时,读者将很难看到特定标识符的来源。using namespace std;
您可能不会意识到您抓取的所有内容 - 当您添加另一个>#include
或迁移到新的 C++ 修订版,您可能会遇到您不知道的名称冲突。您可以在本地使用它
继续并在本地(几乎)自由地使用它。当然,这可以防止您重复
std::
——而且重复也是不好的。在本地使用它的习惯用法
在C++03中,有一个习惯用法——样板代码——用于为类实现
交换
函数。建议您实际使用本地using namespace std;
——或者至少using std::swap;
:这会产生以下魔力:
std::swap
代表value_
,即void std::swap(int, int)
。void swap(Child&, Child&)
,编译器将选择它。对于C++11,没有理由再使用这种模式。
std::swap
的实现已更改,以查找潜在的重载并选择它。Do not use it globally
It is considered "bad" only when used globally. Because:
using namespace xyz;
.using namespace std;
you might not be aware of all the stuff you grab -- and when you add another#include
or move to a new C++ revision you might get name conflicts you were not aware of.You may use it locally
Go ahead and use it locally (almost) freely. This, of course, prevents you from repetition of
std::
-- and repetition is also bad.An idiom for using it locally
In C++03 there was an idiom -- boilerplate code -- for implementing a
swap
function for your classes. It was suggested that you actually use a localusing namespace std;
-- or at leastusing std::swap;
:This does the following magic:
std::swap
forvalue_
, i.e.void std::swap(int, int)
.void swap(Child&, Child&)
implemented the compiler will choose it.void std::swap(Child&,Child&)
and try its best swapping these.With C++11 there is no reason to use this pattern any more. The implementation of
std::swap
was changed to find a potential overload and choose it.如果导入正确的头文件,您会突然得到类似
hex
,左
,plus
或count
在您的全局范围内。如果您不知道std::
包含这些名称,这可能会令人惊讶。如果您还尝试在本地使用这些名称,可能会导致相当多的混乱。如果所有标准内容都在其自己的名称空间中,则不必担心与代码或其他库的名称冲突。
If you import the right header files you suddenly have names like
hex
,left
,plus
orcount
in your global scope. This might be surprising if you are not aware thatstd::
contains these names. If you also try to use these names locally it can lead to quite some confusion.If all the standard stuff is in its own namespace you don't have to worry about name collisions with your code or other libraries.
另一个原因是惊喜。
如果我看到
cout << blah
,而不是std::cout <<等等
我想:这个cout
是什么?这是正常的cout
吗?有什么特别的吗?Another reason is surprise.
If I see
cout << blah
, instead ofstd::cout << blah
I think: What is thiscout
? Is it the normalcout
? Is it something special?我同意它不应该在全局使用,但在本地使用也没有那么邪恶,就像在
命名空间
中一样。以下是“C++ 编程语言” 中的一个示例:在此示例中,我们解决了由于其组合而产生的潜在名称冲突和歧义。
在那里显式声明的名称(包括使用诸如
His_lib::String
之类的 using 声明声明的名称)优先于通过 using 指令(using namespace Her_lib
)在另一个作用域中访问的名称)。I agree that it should not be used globally, but it's not so evil to use locally, like in a
namespace
. Here's an example from "The C++ Programming Language":In this example, we resolved potential name clashes and ambiguities arising from their composition.
Names explicitly declared there (including names declared by using-declarations like
His_lib::String
) take priority over names made accessible in another scope by a using-directive (using namespace Her_lib
).有经验的程序员使用任何可以解决问题的方法,并避免任何产生新问题的方法,并且出于这个确切的原因,他们避免使用头文件级的 using 指令。
经验丰富的程序员也会尽量避免在源文件中对名称进行完全限定。造成这种情况的一个次要原因是,当更少的代码就足够时,编写更多的代码并不优雅,除非有充分的理由。造成这种情况的一个主要原因是关闭参数相关查找(ADL)。
这些充分的理由是什么?有时程序员明确想要关闭 ADL,有时他们想要消除歧义。
因此,以下内容是可以的:
Experienced programmers use whatever solves their problems and avoid whatever creates new problems, and they avoid header-file-level using-directives for this exact reason.
Experienced programmers also try to avoid full qualification of names inside their source files. A minor reason for this is that it's not elegant to write more code when less code is sufficient unless there are good reasons. A major reason for this is turning off argument-dependent lookup (ADL).
What are these good reasons? Sometimes programmers explicitly want to turn off ADL, other times they want to disambiguate.
So the following are OK:
我也认为这是一种不好的做法。为什么?就在有一天,我认为命名空间的功能是划分东西,所以我不应该把所有东西都扔进一个全局包里,从而破坏它。
但是,如果我经常使用“cout”和“cin”,我会写:
using std::cout;在 .cpp 文件中使用 std::cin;
(切勿在头文件中使用,因为它通过#include
传播)。我认为没有一个理智的人会将流命名为cout
或cin
。 ;)I also consider it a bad practice. Why? Just one day I thought that the function of a namespace is to divide stuff, so I shouldn't spoil it with throwing everything into one global bag.
However, if I often use 'cout' and 'cin', I write:
using std::cout; using std::cin;
in the .cpp file (never in the header file as it propagates with#include
). I think that no one sane will ever name a streamcout
orcin
. ;)很高兴看到代码并知道它的作用。如果我看到
std::cout
我就知道这是std
库的cout
流。如果我看到cout
那我就不知道了。它可能是std
库的cout
流。或者同一函数中可能有十行以上的int cout = 0;
。或者该文件中名为cout
的static
变量。它可以是任何东西。现在,采用一百万行代码库,这并不是特别大,并且您正在寻找错误,这意味着您知道这一百万行中有一行没有执行其应该执行的操作。
cout << 1;
可以读取一个名为cout
的static int
,将其左移一位,然后丢弃结果。寻找错误,我必须检查一下。你能看出我是多么喜欢看到std::cout
吗?如果您是一名教师并且从未以编写和维护任何代码为生,那么这似乎是一个非常好的主意。我喜欢看到以下代码:(1) 我知道它的作用;并且,(2) 我相信编写它的人知道它的作用。
It's nice to see code and know what it does. If I see
std::cout
I know that's thecout
stream of thestd
library. If I seecout
then I don't know. It could be thecout
stream of thestd
library. Or there could be anint cout = 0;
ten lines higher in the same function. Or astatic
variable namedcout
in that file. It could be anything.Now take a million line code base, which isn't particularly big, and you're searching for a bug, which means you know there is one line in this one million lines that doesn't do what it is supposed to do.
cout << 1;
could read astatic int
namedcout
, shift it to the left by one bit, and throw away the result. Looking for a bug, I'd have to check that. Can you see how I really really prefer to seestd::cout
?It's one of these things that seem a really good idea if you are a teacher and never had to write and maintain any code for a living. I love seeing code where (1) I know what it does; and, (2) I'm confident that the person writing it knew what it does.
这一切都是为了管理复杂性。使用命名空间会引入您不想要的东西,因此可能会使调试变得更加困难(我说可能)。到处使用 std:: 更难阅读(更多文本等等)。
课程马匹 - 以您最好的方式和感觉能够管理您的复杂性。
It's all about managing complexity. Using the namespace will pull things in that you don't want, and thus possibly make it harder to debug (I say possibly). Using std:: all over the place is harder to read (more text and all that).
Horses for courses - manage your complexity how you best can and feel able.
请
注意,这是一个简单的示例。如果您的文件包含 20 个包含项和其他导入项,那么您将需要检查大量依赖项才能找出问题。更糟糕的是,根据冲突的定义,您可能会在其他模块中遇到不相关的错误。
这并不可怕,但是如果不在头文件或全局命名空间中使用它,您会省去很多麻烦。在非常有限的范围内执行此操作可能没问题,但我在输入额外的五个字符来澄清我的函数来自何处时从未遇到过问题。
Consider
Note that this is a simple example. If you have files with 20 includes and other imports, you'll have a ton of dependencies to go through to figure out the problem. The worse thing about it is that you can get unrelated errors in other modules depending on the definitions that conflict.
It's not horrible, but you'll save yourself headaches by not using it in header files or the global namespace. It's probably all right to do it in very limited scopes, but I've never had a problem typing the extra five characters to clarify where my functions are coming from.
一个具体的例子来澄清这个问题。想象一下,您有两个库:
foo
和bar
,每个库都有自己的命名空间:现在假设您使用
foo
和 < code>bar 一起在你自己的程序中如下:此时一切都很好。当您运行程序时,它会“执行某些操作”。但稍后您更新了
bar
,假设它已更改为:此时您将收到编译器错误:
因此您需要进行一些维护来澄清 'a' 的含义 <代码>foo::a。这是不可取的,但幸运的是它非常简单(只需在编译器标记为不明确的所有对
a
的调用前面添加foo::
即可)。但想象一下另一种情况,其中 bar 改为如下所示:
此时,您对
a(42)
的调用突然绑定到bar::a
而不是>foo::a
并没有做“某事”,而是做“完全不同的事情”。没有编译器警告或任何东西。你的程序只是默默地开始做一些与以前完全不同的事情。当您使用命名空间时,您将面临这样的情况的风险,这就是人们使用命名空间感到不舒服的原因。命名空间中的事物越多,冲突的风险就越大,因此与其他命名空间相比,人们可能会更不舒服地使用命名空间
std
(由于该命名空间中的事物数量)。最终,这是可写性与可靠性/可维护性之间的权衡。可读性也可能是一个因素,但我可以看到无论哪种方式的争论。通常我会说可靠性和可维护性更重要,但在这种情况下,您将不断为相当罕见的可靠性/可维护性影响付出可写性成本。 “最佳”权衡将取决于您的项目和优先事项。
A concrete example to clarify the concern. Imagine you have a situation where you have two libraries,
foo
andbar
, each with their own namespace:Now let's say you use
foo
andbar
together in your own program as follows:At this point everything is fine. When you run your program it 'Does something'. But later you update
bar
and let's say it has changed to be like:At this point you'll get a compiler error:
So you'll need to do some maintenance to clarify that 'a' meant
foo::a
. That's undesirable, but fortunately it is pretty easy (just addfoo::
in front of all calls toa
that the compiler marks as ambiguous).But imagine an alternative scenario where bar changed instead to look like this instead:
At this point your call to
a(42)
suddenly binds tobar::a
instead offoo::a
and instead of doing 'something' it does 'something completely different'. No compiler warning or anything. Your program just silently starts doing something completely different than before.When you use a namespace you're risking a scenario like this, which is why people are uncomfortable using namespaces. The more things in a namespace, the greater the risk of conflict, so people might be even more uncomfortable using namespace
std
(due to the number of things in that namespace) than other namespaces.Ultimately this is a trade-off between writability vs. reliability/maintainability. Readability may factor in also, but I could see arguments for that going either way. Normally I would say reliability and maintainability are more important, but in this case you'll constantly pay the writability cost for an fairly rare reliability/maintainability impact. The 'best' trade-off will determine on your project and your priorities.
您需要能够阅读由与您有不同风格和最佳实践观点的人编写的代码。
如果您只使用
cout
,没有人会感到困惑。但是,当您有很多命名空间,并且您看到此类并且不确定它的作用时,显式命名空间将充当某种注释。乍一看,您可以看到“哦,这是一个文件系统操作”或“这是一个网络操作”。You need to be able to read code written by people who have different style and best practices opinions than you.
If you're only using
cout
, nobody gets confused. But when you have lots of namespaces flying around and you see this class and you aren't exactly sure what it does, having the namespace explicit acts as a comment of sorts. You can see at first glance, "oh, this is a filesystem operation" or "that's doing network stuff".同时使用许多命名空间显然会导致灾难,但在我看来,仅使用命名空间
std
并且仅使用命名空间std
并不是什么大问题,因为重新定义可以仅由您自己的代码发生...因此,只需将它们视为保留名称(如“int”或“class”)即可。
人们不应该再对此如此耿耿于怀。你的老师一直都是对的。只使用一个命名空间;这就是首先使用名称空间的全部意义。您不应该同时使用多个。除非是你自己的。因此,重新定义不会发生。
Using many namespaces at the same time is obviously a recipe for disaster, but using JUST namespace
std
and only namespacestd
is not that big of a deal in my opinion because redefinition can only occur by your own code...So just consider them functions as reserved names like "int" or "class" and that is it.
People should stop being so anal about it. Your teacher was right all along. Just use ONE namespace; that is the whole point of using namespaces the first place. You are not supposed to use more than one at the same time. Unless it is your own. So again, redefinition will not happen.
我同意这里其他人的观点,但我想解决有关可读性的问题 - 您可以通过简单地在文件、函数或类声明的顶部使用 typedef 来避免所有这些问题。
我通常在类声明中使用它,因为类中的方法倾向于处理类似的数据类型(成员),而 typedef 是分配在类上下文中有意义的名称的机会。这实际上有助于类方法定义的可读性。
并在实施中:
与:
或:
I agree with the others here, but I would like to address the concerns regarding readability - you can avoid all of that by simply using typedefs at the top of your file, function or class declaration.
I usually use it in my class declaration as methods in a class tend to deal with similar data types (the members) and a typedef is an opportunity to assign a name that is meaningful in the context of the class. This actually aids readability in the definitions of the class methods.
and in the implementation:
as opposed to:
or: