我听说using namespace std;
是不好的做法,应该直接使用std::cout
和std::cin
。这是为什么呢?是否会冒险声明与 std
命名空间中某些东西同名的变量?
我听说using namespace std;
是不好的做法,应该直接使用std::cout
和std::cin
。这是为什么呢?是否会冒险声明与 std
命名空间中某些东西同名的变量?
考虑两个名为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()
就不会产生影响。
error
、list
、sort
),因此将其放入自己的命名空间中是一个重要的原因。请注意,这里的“将其放入自己的命名空间中”是指将标准库放入std
命名空间中。 - sbi比Greg写的更糟糕的情况可能会发生!
库Foo 2.0可以引入一个函数Quux()
,对于你调用Quux()
的一些参数,它是一个明显更好的匹配,而不是你的代码多年来调用的bar::Quux()
。然后你的代码仍然编译,但它会静默调用错误的函数,并执行神马操作谁也不知道。这就是最糟糕的事情了。
请记住,std
命名空间有大量标识符,其中许多是非常常见的(例如list
、sort
、string
、iterator
等),很可能也出现在其他代码中。
如果你认为这不太可能:在Stack Overflow上曾经有一个问题问到,几乎完全是因为省略了std::
前缀而导致调用错误的函数,这发生在我回答这个问题大约半年后。这里是另一个更近期的类似问题的例子。所以这是一个真正的问题。
using
指令和声明,除了函数范围内。你猜怎么着?我们大多数人只用了很少几周就习惯了写前缀,再过几周,我们大多数人甚至认为它实际上使代码更易读。原因很简单:无论你喜欢短文还是长文都是主观的,但前缀客观地增加了代码的清晰度。不仅编译器,而且你也会发现很容易看出引用了哪个标识符。using
实际上被在项目中使用了多少次。我在源代码中搜索并只找到了一两十个使用它的地方。对我来说,这表明,一旦尝试过,即使在允许使用的情况下,开发人员也不觉得std::
很痛苦,甚至在每100 kLoC中也不会使用一次。
foo
而不是 Foo
?静态方法也应该被称为 Foo::Bar
而不是 Foo::bar
。这就是为什么人们认为规范是一件好事的原因。 - Stefan Riedelfoo::bar
作为静态方法仍然不能反驳解释观点。它仍然更清晰地表明该函数/方法属于哪个类,如果你给你的类起一个好的名称,仍然很清楚表示这是一个类而不是命名空间。 - Stefan Riedelusing namespace
的问题在于,它会强制任何想要使用你的类(通过包含你的头文件)的人也“使用”(即查看所有内容)那些其他命名空间。using
语句比在头文件中使用更好(因为它不会影响包含您的头文件的人),但他们认为它仍然不是很好(因为根据代码的情况,它可能会使类的实现更难以维护)。这篇C ++超级常见问题解答说,
using指令存在于遗留的C++代码和简化过渡到命名空间,但你可能不应该在正常情况下使用它,至少不在你的新C++代码中。
常见问题解答建议两个替代方案:
A using-declaration:
using std::cout; // a using-declaration lets you use cout without qualification
cout << "Values:";
Just typing std::
std::cout << "Values:";
最近我遇到了一项有关Visual Studio 2010的投诉。结果发现几乎所有源文件都包含以下两行:
using namespace std;
using namespace boost;
许多Boost特性正在被纳入C++0x标准,而Visual Studio 2010有很多C++0x特性,因此这些程序突然无法编译。
因此,避免使用using namespace X;
是一种未来保护的形式,一种确保所使用的库和/或头文件的更改不会破坏程序的方法。
using
,而且很少使用 using namespace
。 - Ferruccio简短版:在头文件中不要使用全局的using
声明或指令,但在实现文件中可以自由使用。以下是Herb Sutter和Andrei Alexandrescu在《C++编程规范》中对这个问题的看法(加粗部分是我强调的):
总结
命名空间 using 为你方便而生,不应该被你强加给别人:永远不要在 #include 指令前写 using 声明或指令。
推论:在头文件中,不要写命名空间级别的 using 指令或声明;而是显式地命名空间限定所有名称。(第二条规则是从第一条得出的,因为头文件永远不知道可能在它们之后出现的其他头文件 #include)。
讨论
简而言之:在 #include 指令后你可以自由使用命名空间 using 声明和指令,并且感觉良好。尽管有反复的说法,命名空间 using 声明和指令不是邪恶的,并且它们并没有破坏命名空间的目的。相反,它们使命名空间可用。
using namespace
就像 goto
一样邪恶。两者都有其合理的用途,但在 1000 次中会有 999 次被错误使用。是的,在源代码中使用 using namespace
不会污染其他引用的命名空间,很整洁。但是它仍然不能保护你免受 using namespace Foo
+ using namespace Bar
带来的麻烦,例如你调用(隐式 Foo::)baz(xyz)
,突然之间代码崩溃了(没有任何相关更改),因为 Bar::baz()
在别处添加了,而它刚好匹配得更好(因此现在被调用)。 - CharonXFoo :: baz()
的源文件实际上#include
声明了Bar :: baz()
的头文件,那么代码不会崩溃吗?这似乎不太可能发生。这就像我在main.cpp文件中编写using namespace std;
,但没有#include <iostream>
,然后我仍然可以在main.cpp中定义一个名为cout
的函数,并且不会发生冲突。 - AdmiralAdama#include <iostream>
,std::cout
也可能在作用域内,如果您现在编写using namespace std;
,则您的代码在某些平台上运行并在其他平台上崩溃,具体取决于一个系统头文件是否包含另一个系统头文件的细节(请注意,只需要一个头文件#include <iosfwd>
,这个头文件几乎是为了从其他头文件中包含而存在的)。 - Ben Voigt在全局范围,特别是头文件中,不应该使用 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::sin
,std::cos
……)更好,因为它更短,并且具有通过参数相关的查找(ADL)与用户定义的浮点类型一起使用的能力。
using std :: cos;
, using std :: sin
等等。但问题在于,任何设计良好的userlib
都会将他们自己的sine
和cosine
放在自己的命名空间中,因此这并没有真正帮到你。(除非在此模板之前有一个using namespace userlib
,但是这样做就跟using namespace std
一样糟糕--而且那里的作用域是不受限制的)。此外,我只看到类似这样的函数出现在swap
中,并且在这种情况下,我建议只需创建std:: swap
的模板特化版本,避免整个问题。 - Billy ONealtemplate<typename T> void swap(MyContainer<T>&, MyContainer<T>&)
意为“交换两个 MyContainer<T> 类型的对象”,由于缺乏函数模板偏特化,有时需要使用重载来实现。 - sbix
有一个或多个“关联命名空间”(例如,如果它定义在namespace userlib
中),那么任何看起来像cos(x)
的函数调用都会额外查找这些命名空间——而不需要先使用using namespace userlib;
。Zan Lynx是正确的(而C++名称查找非常复杂…)。 - j_random_hackerusing std::sin; using std::cos; using std::exp;
,而不是 using namespace std;
。这样可以获得相同的好处,而避免了将 std::*
放入函数中所带来的风险。 - Ferruccio只有在全局使用时才被认为是“不好的”。因为:
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(...)`
}
这里的魔法操作如下:
value_
的std::swap
,即void std::swap(int, int)
。void swap(Child&, Child&)
的重载,则编译器将选择它。void std::swap(Child&, Child&)
并尽力交换它们。使用C++11后,不再需要使用此模式。 std::swap
的实现已更改为查找潜在的重载并选择它。
swap
不再那么重要,因为std::swap
本身更灵活(使用移动语义),但是std::swap
自动选择您自己的自定义交换,这对我来说是绝对新的事情(我真的不相信)。 - Christian Rauusing std::swap;
而不是using namespace std;
。这种更具体的习惯用法副作用较少,因此使代码更易于维护。 - Adrian McCarthyusing std::swap
惯用法即使涉及非惯用代码也是可用的。但我同意,当您对源代码有100%的控制权时,最好具体说明。 - towiswap
的正确方式,并且标准中的其他各个地方也被更改为以那种方式调用 swap
(请注意,如上所述,使用using std::swap
是正确的方式,而不是 using namespace std
)。但是,std::swap
本身明确未更改以查找其他 swap
并使用它。如果调用了 std::swap
,则将使用 std::swap
。 - Jonathan Wakelyusing std::swap
可能更明智。你很少对整个 std 命名空间感兴趣,所以只需挑选你感兴趣的部分即可。 - Lundin<iomanip>
,你就没有大多数这些内容。不过,说得好。 - einpoklum<iomanip>
来获取那些内容。在 GCC 中,包含 <iostream>
即可满足所有这些需求,例如 https://gcc.godbolt.org/z/Kqx9q1 - Ayxan Haqverdili<iomanip>
来处理需要参数的操作符,例如setw
。 - celticminstrelcout << blah
,而不是 std::cout << blah
,我会想:这个 cout
是什么?它是普通的 cout
吗?还是特殊的东西?我认为它不应该在全局范围内使用,但是在本地使用并不那么恶劣,比如在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-declarations 声明的名称,例如 His_lib::String)优先于使用 using-directive(如 using namespace Her_lib)在另一个作用域中访问的名称。{..}
来定义命名空间的范围。 - Ol Sen
using
命令,除非针对特定的命名空间,例如std::literals::chrono_literals
、Poco::Data:Keywords
、Poco::Units
等与字面值或可读性技巧有关的内容。无论是在头文件还是实现文件中都不应该使用。在函数作用域内可能还可以接受,但除了字面值和相关内容外都没有用处。 - Ludovic Zenohate Lagouardette