使用std命名空间
对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
的局部variables意味着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
是一个长嵌套types的模板。
这是好的,因为std::count
进入全局命名空间,函数计数隐藏它。
#include <algorithm> using namespace std; int increment() { static int count = 0; return ++count; }
也许有点出人意料,这是好的。 导入到声明性范围中的标识符出现在通用名称空间中,该通用名称空间包含它们的定义位置和导入位置。 换句话说, std::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;
相同的范围内int count = 0;
并因此造成歧义。
#include <algorithm> int count = 0; int increment() { using namespace std; return ++count; // error ambiguous }
不包括基础知识(如果你没有'使用命名空间std',必须添加所有stl对象/函数的std :: infront,冲突的可能性更小)
还值得注意的是,你永远不应该放
using namespace std
在头文件中,因为它可以传播到包含该头文件的所有文件,即使它们不想使用该名称空间。
在某些情况下,使用类似的东西是非常有益的
using std::swap
就好像有一个专门的swap版本,编译器会使用它,否则会回退到std :: swap
如果您调用std :: swap,则始终使用基本版本,而不会调用优化版本(如果存在)。
首先,一些术语:
- 使用声明 :
using std::vector;
- using-directive :
using namespace std;
我认为使用using指令是好的,只要它们不在头文件的全局范围中使用。 所以有
using namespace std;
在你的.cpp文件中并不是一个真正的问题,如果事实certificate,它完全在你的控制之下(如果需要的话,它甚至可以被限定到特定的块)。 我没有看到用std::
qualifiers混淆代码的特殊原因 – 它只是成为一堆视觉噪声。 但是,如果你没有在你的代码中使用std
命名空间中的大量名字,我也没有看到这个指令的问题。 这是一个同义反复 – 如果指令不是必要的,那么就没有必要使用它。
同样的,如果你可以通过一些使用声明 (而不是使用指令 )来获得std
命名空间中的specfictypes,那么没有理由不应该把这些特定的名字带入当前的命名空间。 同样的道理,我认为当一个使用指令也可以做到这一点时,会有25个或30个使用声明是疯狂的和簿记的麻烦。
记住有时候你必须使用using声明也是很好的。 请参考Scott Meyers在Effective C ++第三版中的“项目25:考虑支持非抛出交换”。 为了使generics的模板化函数对参数化types使用“最佳”交换方法,您需要使用使用声明和参数相关查找(又名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等同地放到当前的作用域中。 这不会导致大范围的问题,并且几次它是一个问题,通过完全限定的名字或者通过别名处理名称来处理exception,就像在C ++中可以做到的那样。
Herb Sutter和Andrei Alexandrescu在“项目59:不要在头文件中编写名称空间或者在编写#include”之前写下“C ++编码标准:规则,准则和最佳实践:
简而言之:在
#include
指令之后,您可以并且应该在您的实现文件中宽泛地使用声明和指令来使用名称空间,并且感觉良好。 尽pipe反复声明,使用声明和指令的命名空间并不是邪恶的,它们并没有打败命名空间的目的。 相反,它们是使命名空间可用的。
在“C ++编程语言,第三版”中经常引用Stroupstrup的话:“不要污染全局命名空间”。 他事实上是说(C.14 [15]),而是指C.10.1章,他说:
使用声明将名称添加到本地作用域。 使用指令不会; 它只是简单地在名称范围内呈现名称。 例如:
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 }
本地声明的名称(由普通声明或使用声明声明)隐藏相同名称的非本地声明,并且在声明处检测到名称的任何非法重载。
注意
f1()
k++
的模糊性错误。 全局名称不会优先于全局范围内可访问的名称空间中的名称。 这提供了重大的保护,防止意外的名称冲突,并且 – 重要的是 – 确保从污染全球名称空间中获得没有好处。当声明多个名称的库可以通过使用指令访问时,这是一个重要的优势,未使用名称的冲突不被视为错误。
…
与传统的C和C ++程序相比,我希望在使用名称空间的新程序中全局名称的使用大幅减less。 命名空间的规则是专门devise的,它不会给全球域名的“懒惰”用户带来不利于全球范围的问题。
那么,作为一个“全球名字的懒惰用户”,它有什么样的优势呢? 通过利用using指令,可以安全地使名称空间中的名称可用于当前作用域。
请注意,有一个区别 – 通过正确使用using-directive(通过将指令放置在#includes
), std
名称空间中的名称可用于作用域,这不会污染全局名称空间。 只是简单地提供这些名称,并持续保护免受冲突。
切勿在头文件中在全局范围使用名称空间。 这可能导致冲突,冲突出现的文件负责人无法控制事件。
在实施文件中,select是不太好的。
-
把一个使用命名空间std带来了来自这个命名空间的所有符号。 这可能是麻烦的,因为几乎没有人知道那里所有的符号(所以在实践中不可能适用不冲突的政策),而不用说要添加的符号。 而C ++标准允许头部添加来自其他头部的符号(C不允许)。 在实践中,在受控情况下简化写作仍然可行。 如果发生错误,则在有问题的文件中检测到。
-
使用std :: name; 具有书写简单而没有导入未知符号的风险的优点。 成本是你必须显式导入所有想要的符号。
-
明确的排位加点混乱,但我认为这是一些练习较less的麻烦。
在我的项目中,我使用显式限定所有名称,我接受使用std :: name,我反对使用命名空间标准(我们有一个lisp解释器有自己的列表types,所以冲突是肯定的事情)。
对于其他名称空间,您还必须考虑使用的命名约定。 我知道一个项目使用命名空间(版本)和名称前缀。 然后做一个using namespace X
几乎没有风险,不这样做会导致愚蠢的代码PrefixNS::pfxMyFunction(...)
。
有些情况下你想要导入符号。 std :: swap是最常见的情况:你导入std :: swap然后使用swap不合格。 依赖于参数的查找将在types的名称空间中find适当的交换(如果有),并返回到标准模板(如果没有)。
编辑:
在评论中,迈克尔·伯尔想知道冲突是否发生在现实世界中。 这是一个真实的例证。 我们有一个扩展语言是一个口语方言。 我们的解释器有一个include文件,包含lisp.h
typedef struct list {} list;
我们必须整合和调整一些看起来像这样的代码(我将其命名为“引擎”):
#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”。 testing通过了。 “模块”没有被修改,影响了它的ABI,所以“引擎”库可以在不重新编译用户的情况下使用。 集成testing是可以的。 新的“模块”发布。 当代码没有被修改的时候,下一次编译引擎就崩溃了。
都
using std::string;
和
using namespace std;
将一些符号(一个或多个)添加到全局名称空间。 在全局命名空间中添加符号是你永远不应该在头文件中做的事情。 你没有控制谁将包括你的头,有很多包含其他头的头(以及包含头的头包含头等等)。
在实现(.cpp)文件中取决于你(只记得在所有的#include指令之后)。 你只能打破这个特定文件中的代码,所以pipe理和查找名称冲突的原因更容易。 如果你喜欢在标识符前使用std ::(或者其他的前缀,在你的项目中可以有许多命名空间),那就OK了。 如果你想添加你使用全局命名空间的标识符,那就OK了。 如果你想把整个命名空间放在你的头上:-),这取决于你。 虽然效果仅限于单个编译单元,但是可以接受。
如果您在使用std和其他库的代码中没有名称冲突的风险,则可以使用:
using namespace std;
但是,如果您想精确知道代码对文档的依赖性,或者存在名称冲突的风险,请使用其他方式:
using std::string; using std::cout;
第三个解决scheme,不要使用这些解决scheme,并在每次使用代码之前写入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
一样using std::vector
声明添加本地。 但问问自己:这有什么价值? 一行代码被写入一次(也许两次),但是它被读取了十次,一万次或者几千次。 与读取代码的努力相比,保存的打字努力是添加使用声明或指令是边际的。
考虑到这一点,在十年前的一个项目中,我们决定明确限定所有标识符的完整名称空间名称。 刚开始在两周内变得日常尴尬。 现在,在整个公司的所有项目中,没有人正在使用指令或声明。 (有一个例外,见下文。)十年后,看看代码(几个MLoC),我觉得我们做出了正确的决定。
我发现通常情况下,反对禁止using
通常没有尝试过一个项目。 那些已经尝试过的人经常发现它比在很短的时间内使用指令/声明更好。
注意:唯一的例外是using std::swap
,这是必须的(特别是在generics代码中)来获取swap()
重载,这个重载不能被放到std
名字空间中(因为我们不允许把std
重载进入这个命名空间)。
using namespace std
导入当前命名空间中的std
命名空间的内容。 因此,好处是你不必在那个名字空间的所有函数前inputstd::
。 但是,可能会发生具有不同名称空间的命名空间。 因此,你可能不会打电话给你想要的。
在std
手动指定要导入哪些内容可以防止这种情况发生,但是可能会导致在文件开始处使用很长的列表,这有些开发人员会觉得很丑陋;)!
就个人而言,我更喜欢每次使用函数时指定命名空间,除非命名空间太长,在这种情况下,我会在文件的开始部分使用一些命令空间。
编辑:正如在另一个答案中指出的,你不应该把一个using namespace
放在一个头文件中,因为它会传播到包括这个头文件在内的所有文件,因此可能会产生不需要的行为。
编辑2:纠正我的答案,感谢查尔斯评论。
命名空间保留代码以防止function签名的混淆和污染 。
这里有一个完整的和有logging的适当的 命名空间使用示例:
#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。*或者简单地select每个类,这取决于样式。 请注意,您不希望在文件/宽范围的开始处using namespace std
,因为您将污染命名空间并可能发生冲突,从而破坏命名空间的点。 但是如果你有一个使用了很多STL的函数,那么在你的逻辑中混乱了代码的前缀语法,你应该考虑using namespace std
(当使用各种类时)或者个人using
s当经常使用几个类时)。
只要您使用的IDE不够灵活,无法显示或隐藏所需的确切信息,这个讨论就会一直存在。
那是因为你想让你的代码看起来像取决于手头的任务。
在创build我的源代码时,我更喜欢看到我正在使用哪个类:它是std::string
还是BuzFlox::Obs::string
类?
在devise控制stream程的时候,我甚至对variables的types不感兴趣,但是我希望把重点放在if
一切都在continue
。
所以这是我的build议:
根据代码的受众和工具的强大程度,select最简单的方式,或者提供大部分信息。
有几种方法可以解决这个问题。
首先:像你所做的一样使用。
第二: namespace S = std;
,减less2个字符。
第三:使用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&);