关于std命名空间使用'using'似乎有不同的看法。
有人说使用'using namespace std
',其他人说不要,而是使用与'std::
'一起使用的前缀std函数,而其他人则说使用这样的东西:
using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::vector;
对于要使用的所有std函数。
各自的优点和缺点是什么?
大多数C ++用户都非常高兴阅读std::string
,std::vector
等。事实上,看到原始的vector
让我想知道这是std::vector
还是用户定义的vector
。
我总是反对使用using namespace std;
。它将各种名称导入全局命名空间,并可能导致各种非明显的歧义。
以下是std
命名空间中的一些常用标识符:count,sort,find,equal,reverse。有一个名为count
的局部变量意味着using namespace std
不会让你使用count
而不是std::count
。
不需要的名称冲突的典型示例如下所示。想象一下,你是一个初学者,不知道std::count
。想象一下,你要么在<algorithm>
中使用其他东西,要么被一个看似无关的标题所吸引。
#include <algorithm>
using namespace std;
int count = 0;
int increment()
{
return ++count; // error, identifier count is ambiguous
}
错误通常很长且不友好,因为std::count
是一个具有一些长嵌套类型的模板。
这是可以的,因为std::count
进入全局命名空间并且函数计数隐藏它。
#include <algorithm>
using namespace std;
int increment()
{
static int count = 0;
return ++count;
}
也许有点令人惊讶,这是可以的。导入到声明性作用域中的标识符出现在公共名称空间中,该名称空间包含它们的定义位置和导入位置。换句话说,std::count
在全局命名空间中显示为count
,但仅在increment
中。
#include <algorithm>
int increment()
{
using namespace std;
static int count = 0;
return ++count;
}
由于类似的原因,count
在这里含糊不清。 using namespace std
不会导致std::count
,隐藏外部count
,因为它可能是预期的。 using namespace
规则意味着std::count
看起来(在increment
函数中)好像它是在全球范围内宣布的,即与int count = 0;
在相同的范围内,因此导致模糊性。
#include <algorithm>
int count = 0;
int increment()
{
using namespace std;
return ++count; // error ambiguous
}
命名空间保留代码以防止函数签名的confusion和pollution。
Here是proper namespace用法的完整文档演示:
#include <iostream>
#include <cmath> // Uses ::log, which would be the log() here if it were not in a namespace, see https://stackoverflow.com/questions/11892976/why-is-my-log-in-the-std-namespace
// Silently overrides std::log
//double log(double d) { return 420; }
namespace uniquename {
using namespace std; // So we don't have to waste space on std:: when not needed.
double log(double d) {
return 42;
}
int main() {
cout << "Our log: " << log(4.2) << endl;
cout << "Standard log: " << std::log(4.2);
return 0;
}
}
// Global wrapper for our contained code.
int main() {
return uniquename::main();
}
输出:
Our log: 42
Standard log: 1.43508
就像在Java中你可以使用的那样,可以包括java.util。*或者只是单独选择每个类,它取决于样式。请注意,您不希望在文件/宽范围的开头有一个using namespace std
,因为您将污染命名空间并可能发生冲突,从而破坏了命名空间点。但是如果你有一个使用大量STL的函数,它会使代码混乱,在你的逻辑中混杂使用前缀语法,你应该考虑使用using namespace std
(当使用各种类时)或单独的using
s(当使用时经常很少上课)。
只要您使用的IDE不够灵活,无法显示或隐藏您需要的确切信息,此讨论将继续存在。
这是因为您希望代码看起来像是取决于手头的任务。
在创建我的源代码时,我更喜欢看到我正在使用哪个类:是std::string
还是BuzFlox::Obs::string
类?
在设计控制流时,我甚至不对变量的类型感兴趣,但我想关注if
和while
和continue
。
所以这是我的建议:
根据您的代码的受众和工具的强大功能,选择最简单的方式或提供大多数信息的方式。
有几种方法可以解决这个问题。
第一:像你所做的一样使用。
第二:做namespace S = std;
,减少2个字符。
第三:使用static
。
第四:不要使用std
使用的名称。
每种方法的优缺点是什么?
离开std ::的唯一原因是,理论上你可以自己重新实现所有的STL函数。然后你的函数可以从使用std :: vector切换到my :: vector而无需更改代码。
举个例子
typedef std::vector<int> ints_t;
ints_t ints1;
....
ints_t ints2;
而不是笨拙的
std::vector<int> ints1;
...
std::vector<int> ints2;
我发现它更具可读性,也是我编码的标准。
您甚至可以使用它为读者包含一些语义信息。例如,考虑函数原型
void getHistorgram(std::vector<unsigned int>&, std::vector<unsigned int>&);
返回值是哪一个?
相反怎么样
typedef std::vector<unsigned int> values_t;
typedef std::vector<unsigned int> histogram_t;
...
void getHistogram(values_t&, histogram_t&);
排除基础知识(必须添加所有stl对象/函数的std :: infront,如果没有'using namespace std',则冲突的可能性更小)
值得注意的是,你永远不应该放
using namespace std
在头文件中,因为它可以传播到包含该头文件的所有文件,即使他们不想使用该命名空间。
在某些情况下,使用像这样的东西是非常有益的
using std::swap
好像有一个专门的swap版本,编译器将使用它,否则它将回退到std :: swap
如果调用std :: swap,则始终使用基本版本,该版本不会调用优化版本(如果存在)。
首先,一些术语:
using std::vector;
using namespace std;
我认为使用using-directives是好的,只要它们不在头文件的全局范围内使用。所以有
using namespace std;
在你的.cpp文件中并不是一个真正的问题,如果事实证明,它完全在你的控制之下(如果需要它甚至可以限定为特定的块)。我没有看到任何特别的理由用一大堆std::
限定符来混淆代码 - 它只是变成了一堆视觉噪音。但是,如果您没有在代码中使用std
命名空间中的一大堆名称,我也发现删除该指令没有问题。这是一个重言式 - 如果指令不是必要的,那么就没有必要使用它了。
类似地,如果你可以在std
命名空间中使用一些using声明(而不是using-directives)来获取特定类型,那么你就没有理由不应该只将那些特定的名称带入当前的命名空间。出于同样的原因,我认为当一个使用指令也可以做到这一点时,拥有25或30个使用声明会是疯狂和簿记的麻烦。
记住有时你必须使用using声明也是很好的。请参阅Scott Meyers的“第25版:考虑支持非投掷互换”,来自Effective C ++,Third Edition。为了使通用的模板化函数对参数化类型使用“最佳”交换方法,您需要使用using声明和参数依赖查找(也称为ADL或Koenig查找):
template< typename T >
void foo( T& x, T& y)
{
using std::swap; // makes std::swap available in this function
// do stuff...
swap( x, y); // will use a T-specific swap() if it exists,
// otherwise will use std::swap<T>()
// ...
}
我认为我们应该看看各种语言的常用习语,它们大量使用命名空间。例如,Java和C#在很大程度上使用命名空间(可能比C ++更多)。在这些语言中使用名称空间中最常见的方式名称是将它们集中到当前作用域中,使用等效的using-directive。这不会导致广泛传播的问题,并且通过完全限定名称或别名来处理有问题的名称,就可以在“异常”的基础上处理问题的几次 - 就像在C ++中完成一样。
Herb Sutter和Andrei Alexandrescu在“项目59:不在头文件中或在#include之前编写命名空间”这一点中说明了他们的书,C ++编码标准:101规则,指南和最佳实践:
简而言之:在
#include
指令之后,你可以而且应该在你的实现文件中使用声明和指令使用命名空间,并且感觉良好。尽管反复断言,使用声明和指令的命名空间并不是邪恶的,并且它们不会破坏命名空间的目的。相反,它们是命名空间可用的原因。
Stroupstrup经常被引用为“不要污染全局命名空间”,在“The C ++ Programming Language,Third Edition”中。事实上,他确实说过(C.14 [15]),但是参考C.10.1章,他说:
using声明为本地范围添加名称。使用指令不;它只是在声明它们的范围内呈现可访问的名称。例如:
namespaceX { int i , j , k ; } int k ; void f1() { int i = 0 ; using namespaceX ; // make names from X accessible i++; // local i j++; // X::j k++; // error: X::k or global k ? ::k ++; // the global k X::k ++; // X’s k } void f2() { int i = 0 ; using X::i ; // error: i declared twice in f2() using X::j ; using X::k ; // hides global k i++; j++; // X::j k++; // X::k }
本地声明的名称(由普通声明或using声明声明)隐藏了同名的非本地声明,并且在声明时检测到名称的任何非法过载。
请注意
k++
中f1()
的歧义错误。全局名称不会优先于全局范围内可访问的名称空间中的名称。这为意外名称冲突提供了重要保护,并且 - 重要的是 - 确保从污染全局命名空间中获得的任何优势都没有。当通过using-directives使可以访问声明许多名称的库时,未使用的名称的冲突不被视为错误是一个重要的优点。
...
我希望与传统的C和C ++程序相比,使用命名空间的新程序中全局名称的使用会大幅减少。命名空间的规则是专门设计的,对于那些注意不要污染全局范围的人而言,对全球名称的“懒惰”用户没有任何好处。
人们如何与“全球名称的懒惰用户”具有相同的优势?通过利用using-directive,它可以安全地使名称空间中的名称可用于当前作用域。
请注意,有一个区别 - std
命名空间中的名称可以通过正确使用using指令(通过在#includes
之后放置指令)使范围可用而不会污染全局命名空间。它只是简单地提供这些名称,并持续防止冲突。
切勿在头文件中使用全局范围内的命名空间。这可能导致冲突,冲突出现的文件负责人无法控制原因。
在实现文件中,选择远没有那么好。
在我的项目中,我对所有名称使用显式限定,我接受使用std :: name,我反对使用命名空间std(我们有一个lisp解释器,它有自己的列表类型,所以冲突是肯定的事情)。
对于其他名称空间,您还必须考虑使用的命名约定。我知道一个项目使用命名空间(用于版本控制)和名称前缀。然后做一个using namespace X
几乎没有风险,不做这导致愚蠢的代码PrefixNS::pfxMyFunction(...)
。
在某些情况下,您要导入符号。 std :: swap是最常见的情况:导入std :: swap然后使用swap unqualified。参数依赖查找将在类型的命名空间中找到适当的交换(如果有)并且如果没有则返回到标准模板。
编辑:
在评论中,Michael Burr想知道冲突是否发生在现实世界中。这是一个真实的例子。我们有一种扩展语言,是一种lisp方言。我们的解释器有一个包含文件,包含lisp.h
typedef struct list {} list;
我们必须集成并调整一些代码(我将其命名为“engine”),如下所示:
#include <list>
...
using std::list;
...
void foo(list const&) {}
所以我们修改如下:
#include <list>
#include "module.h"
...
using std::list;
...
void foo(list const&) {}
好。一切正常。几个月后,“module.h”被修改为包含“list.h”。测试通过了。 “模块”没有以影响其ABI的方式进行修改,因此可以使用“引擎”库而无需重新编译其用户。集成测试没问题。新的“模块”出版。当代码未被修改时,下一次引擎编译就破了。
都
using std::string;
和
using namespace std;
向全局命名空间添加一些符号(一个或多个)。在全局命名空间中添加符号是您在头文件中永远不应该做的事情。您无法控制谁将包含您的标头,有许多标头包含其他标头(以及包含标头的标头,包括标题等等)。
在实现(.cpp)文件中,它取决于你(只记得在所有#include指令之后执行)。您只能破坏此特定文件中的代码,因此更容易管理并找出名称冲突的原因。如果您更喜欢在标识符之前使用std ::(或任何其他前缀,项目中可能有许多名称空间),那就没问题。如果您想将您使用的标识符添加到全局命名空间,那就没问题。如果你想把整个命名空间放在头上:-),这取决于你。虽然效果仅限于单个编译单元,但它是可以接受的。
如果您在使用std和其他库的代码中没有名称冲突的风险,您可以使用:
using namespace std;
但是,如果您想要准确了解代码对文档的依赖性,或者存在名称冲突的风险,请使用另一种方式:
using std::string;
using std::cout;
第三种解决方案,不要使用这些解决方案并在每次使用代码之前编写std ::为您带来更多安全性,但是,代码中可能有点沉重......
对我来说,我更喜欢在可能的情况下使用::
。
std::list<int> iList;
我不想写:
for(std::list<int>::iterator i = iList.begin(); i != iList.end(); i++)
{
//
}
希望,使用C ++ 0x我会写这个:
for(auto i = iList.begin(); i != iList.end(); i++)
{
//
}
如果名称空间很长,
namespace dir = boost::filesystem;
dir::directory_iterator file("e:/boost");
dir::directory_iterator end;
for( ; file != end; file++)
{
if(dir::is_directory(*file))
std::cout << *file << std::endl;
}
你永远不应该在标题的命名空间范围内使用using namespace std
。此外,我想大多数程序员会想知道什么时候他们看到没有vector
的string
或std::
,所以我认为不是using namespace std
更好。因此,我认为永远不会是using namespace std
。
如果您认为必须,请使用using std::vector
等声明添加本地。但问问自己:这有什么价值?一行代码写入一次(可能两次),但它读取十次,一百次或一千次。保存的输入工作是添加使用声明或指令与读取代码的努力相比是边际的。
考虑到这一点,在十年前的一个项目中,我们决定使用其完整的命名空间名称明确限定所有标识符。起初看起来很尴尬的事情在两周之内成了常规。现在,在整个公司的所有项目中,没有人使用指令或声明了。 (有一个例外,见下文。)看看十年后的代码(几个MLoC),我觉得我们做出了正确的决定。
我发现通常,反对禁止using
的人通常没有尝试过一个项目。那些尝试过的人,经常发现它比在很短的时间后使用指令/声明更好。
注意:唯一的例外是using std::swap
,它必须(特别是在通用代码中)获取无法放入swap()
命名空间的std
的重载(因为我们不允许将std
函数的put重载放入此命名空间)。
using namespace std
导入当前std
命名空间的内容。因此,优点是您不必在该命名空间的所有函数前面键入std::
。但是,您可能会遇到具有相同名称功能的不同命名空间。因此,你可能不会打电话给你想要的人。
手动指定要在std
中导入哪些内容可防止发生这种情况,但可能会导致在文件开头使用一长串列表,而某些开发人员会发现这些列表很难看;)!
就个人而言,我更喜欢在每次使用函数时指定命名空间,除非命名空间太长,在这种情况下我在文件的开头使用了一些。
编辑:如另一个答案中所述,你不应该将using namespace
放在头文件中,因为它会传播到包括此头文件在内的所有文件,因此可能会产生不需要的行为。
EDIT2:感谢Charles评论,纠正了我的回答。