关于std名称空间使用“ using”似乎有不同的看法。
有些人说使用' using namespace std
,其他std::
人说不使用,而是给将与' ' 一起使用的std函数添加前缀,而其他人则说使用类似以下内容:
using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::vector;
对于所有要使用的std函数。
各自的优缺点是什么?
关于std名称空间使用“ using”似乎有不同的看法。
有些人说使用' using namespace std
,其他std::
人说不使用,而是给将与' ' 一起使用的std函数添加前缀,而其他人则说使用类似以下内容:
using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::vector;
对于所有要使用的std函数。
各自的优缺点是什么?
Answers:
大多数C ++用户都非常乐于阅读std::string
,std::vector
等等。实际上,看到原始内容vector
使我想知道这是std::vector
用户定义的还是其他用户定义的vector
。
我一直反对使用using namespace std;
。它将各种名称导入到全局名称空间中,并且可能导致各种非显而易见的歧义。
以下是std
命名空间中的一些常见标识符:计数,排序,查找,相等,反向。拥有一个名为的局部变量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
}
using std::xxx;
。它不会造成名称空间污染,编写代码会更短,而且我认为copy
其可读性比得多std::copy
。
不包括基础知识(必须在所有stl对象/函数的前面添加std ::,如果没有“使用命名空间std”,则发生冲突的可能性较小)
还值得注意的是,您永远不要放
using namespace std
在头文件中,因为它可以传播到包含该头文件的所有文件,即使它们不想使用该名称空间也是如此。
在某些情况下,使用诸如
using std::swap
好像有一个特殊版本的swap一样,编译器将使用该版本,否则将依赖于std::swap
。
如果调用std::swap
,则始终使用基本版本,而基本版本不会调用优化版本(如果存在)。
using std::swap
(这是我唯一使用过的东西)。
u n s
可以传播。只是要注意,它也可能会侵入正确构造的标头中:它们只必须包含在恶意标头之后。
swap
或move
(或hash
,less
等等)专业化,则namespace std
无论如何都应将该专业化。例如:namespace std {template<> class hash<X> {public: size_t operator()(const X&) const};} class X: {friend size_t std::hash<X>::operator()(const X&)};
首先,一些术语:
using std::vector;
using namespace std;
我认为,只要不在头文件的全局范围内使用using指令,就可以使用using指令。所以有
using namespace std;
在您的.cpp文件中并不是一个真正的问题,事实证明,这完全在您的控制之下(如果需要,它甚至可以限制在特定的块内)。我看不出有什么特别的理由来用大量的std::
限定词来弄乱代码-它只会变成一堆视觉噪声。但是,如果您没有std
在代码中使用命名空间中的全部名称,那么我也不会忽略该指令。这是一种重言式-如果该指令不是必需的,则无需使用它。
同样,如果您可以在命名空间中使用一些用于特定类型的using- 声明(而不是using-directives)std
,那么就没有理由不应该将那些特定的名称带入当前命名空间中。出于同样的原因,我认为这样做很疯狂,而且当单个using指令也能达到目的时,记帐麻烦到有25或30个use声明。
请记住,有时候您必须使用using-声明。请参阅《有效C ++,第三版》中的Scott Meyers的“第25项:考虑支持非抛出交换”。为了使通用的模板化函数对参数化类型使用“最佳”交换方法,您需要使用使用声明和依赖于参数的查找(又名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 ++更大)。在这些语言中,名称空间中最常见的名称使用方式是将它们与使用指令等效,从而将它们带入当前范围。这不会引起广泛的问题,而且很少会通过完全限定的名称或通过别名处理有问题的名称,以“例外”的方式处理问题,就像在C ++中一样。
Herb Sutter和Andrei Alexandrescu在他们的书《 C ++编码标准:101规则,指南和最佳做法》的“项目59:不要在头文件中或#include之前写名称空间使用情况”中说:
简而言之:您可以并且应该在
#include
指令后的实现文件中自由使用使用声明和指令的名称空间,并对此感到满意。尽管反复断言相反,使用声明和指令的名称空间并不是邪恶的,它们不会破坏名称空间的目的。而是它们使名称空间可用。
Stroupstrup在“ C ++编程语言,第三版”中经常被引用为“不要污染全局名称空间”。他实际上的确是这样说的(C.14 [15]),但他提到的是C.10.1章:
一个using声明增加了一个名字,以局部范围。一个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 }
本地声明的名称(通过普通声明或使用声明声明)隐藏了相同名称的非本地声明,并且在声明时检测到该名称的任何非法重载。
注意
k++
in中 的歧义错误f1()
。全局名称不会优先于全局范围内可访问的命名空间中的名称。这为防止意外的名称冲突提供了重要的保护,并且-重要的是-确保污染全局名称空间不会获得任何好处。当声明许多名称的库可通过使用指令进行访问时,未使用的名称冲突不视为错误是一个重要的优势。
...
我希望看到与传统的C和C ++程序相比,在使用命名空间的新程序中使用全局名称的人数急剧减少。命名空间的规则是经过精心设计的,与那些小心翼翼地避免污染全局范围的人相比,它对全局名称的“懒惰”用户没有任何好处。
以及与“全球名称的懒用户”一样,又有什么优势呢?通过利用using指令,可以安全地使名称空间中的名称可用于当前作用域。
请注意,这是有区别的- std
正确使用using指令(通过将指令放在之后#includes
)可用于范围的名称空间中的名称不会污染全局名称空间。只是使这些名称易于使用,并提供持续的冲突保护。
std::
限定符不会使代码混乱不堪-还有其他方法可以避免这种情况(使用声明或typedef通常可以解决问题)。
using namespace std;
”指令并不会阻止您声明自然标识符' list
'-只是如果这样做,您就不能长时间使用std::list
而不合格。这与没有“ using namespace std;
”指令没有什么不同。还是我错过了什么?
切勿在头文件的全局范围内使用命名空间。这可能导致冲突,并且出现冲突的文件负责人无法控制原因。
在实现文件中,选择的余地要少得多。
放置using名称空间std会带来该名称空间中的所有符号。这可能很麻烦,因为几乎没有人知道其中所有的符号(因此,在实践中不可能应用无冲突的策略)而没有说要添加的符号。C ++标准允许标头添加其他标头中的符号(C语言不允许这样做)。在实践中,仍然可以很好地简化受控案例中的编写。并且如果发生错误,则在有问题的文件中检测到该错误。
使用std :: name; 具有编写简单的优点,而没有导入未知符号的风险。代价是必须显式导入所有需要的符号。
排位赛明显增加了一些混乱,但是我认为这是一些练习的麻烦。
在我的项目中,我对所有名称使用显式限定,我接受使用std :: name,我反对使用命名空间std(我们有一个lisp解释器,该解释器具有自己的列表类型,因此可以肯定有冲突)。
对于其他命名空间,还必须考虑使用的命名约定。我知道一个使用名称空间(用于版本控制)和名称前缀的项目。做一个using namespace X
则是几乎没有风险,并没有这样做会导致傻看代码PrefixNS::pfxMyFunction(...)
。
在某些情况下,您想导入符号。std :: swap是最常见的情况:您导入std :: swap,然后使用不合格的swap。如果有一个,依赖于参数的查找将在类型的命名空间中找到足够的交换,如果没有,则退回到标准模板。
编辑:
在评论中,迈克尔·伯(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::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;
}
++i
,这不是i++
因为即使定义了迭代器也不会创建不必要的临时副本。
您永远不应using namespace std
在标头中的名称空间范围内。另外,我想大多数程序员会怀疑他们何时看到vector
或string
不看到std::
,所以我认为没有using namespace std
更好的选择。因此,我认为绝对不会存在using namespace std
。
如果您觉得必须使用,请使用以下声明添加本地 using std::vector
。但是问自己:这有什么价值?一行代码只被编写一次(也许两次),但是被读取了十,一百或一千次。与读取代码相比,通过添加using声明或指令节省的键入工作量很小。
考虑到这一点,十年前的一个项目中,我们决定使用所有名称空间的完整名称来明确限定所有标识符。起初看起来很尴尬的事情在两周内就变成了常规。现在,在整个公司的所有项目中,没有人再使用指令或声明了。(除了一个例外,请参阅下文。)十年后的代码(几个MLoC)让我觉得我们做出了正确的决定。
我发现通常情况下,那些反对禁令的人using
通常不会在一个项目中尝试过。那些尝试过的人通常会发现,比在很短的时间内使用指令/声明更好。
注意:唯一的例外是using std::swap
必要的(特别是在通用代码中),以拾取swap()
不能放入std
命名空间的重载(因为不允许我们将std
函数的重载放入此命名空间)。
std
,但不能重载。对不起,这个脑袋。我会改正这个帖子。
using namespace
指令的目的不是要进行输入。相反,这是为了使阅读更容易,因为正如您所说,该代码将必须被读取数十,数百或数千次。对于某些人来说,它读起来更容易,std::
混乱也更少。但这可能归结为个人的感知能力;有些人过滤std::
掉甚至需要它作为指导(例如衬线),其他人则踩在上面,感觉就像在崎road不平的道路上。
#include <iostream>
#include <cmath> // Uses ::log, which would be the log() here if it were not in a namespace, see /programming/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
using namespace std
导入std
当前名称空间中名称空间的内容。因此,这样做的好处是您不必std::
在该名称空间的所有函数前面键入内容。但是,可能会发生具有具有相同名称功能的不同名称空间的情况。因此,您可能会结束不拨打您想要的电话的情况。
手动指定要导入的文件std
可以防止这种情况的发生,但是可能会导致在文件开头使用一长串文件,有些开发人员会发现这很丑陋;)!
就个人而言,我更喜欢每次使用函数时都指定名称空间,除非名称空间过长(在这种情况下,我会在文件的开头放置一些use)。
编辑:如另一个答案中所述,您永远不要using namespace
在标头文件中放置,因为它会传播到包括该标头的所有文件,因此可能会产生不良行为。
EDIT2:更正了我的答案,这要感谢Charles的评论。
using namespace std;
将std
名称空间的内容导入全局名称空间。它不会更改默认名称空间。在using namespace std
将其神奇地放入命名空间之后,在全局命名空间中定义某些内容std
。
有几种解决方法。
第一:像你一样使用。
第二:做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&);