关于std命名空间使用'using'似乎有不同的看法。 using namespace std',而其他人则说使用这样的东西:

using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::vector;


用于所有将要使用的std函数。

有什么优点?的利弊?

评论

另请参见如何在C ++中正确使用名称空间?

为什么“使用命名空间标准”被认为是不好的做法?

以及如何解决C ++命名空间与全局函数之间的名称冲突?

#1 楼

大多数C ++用户都很高兴阅读std::stringstd::vector等。实际上,看到原始的vector让我怀疑这是std::vector还是其他用户定义的vector

我始终反对使用using namespace std; 。它将各种名称导入到全局名称空间中,并且可能引起各种非显而易见的歧义。

以下是std名称空间中的一些常见标识符:计数,排序,查找,相等,反向。拥有名为count的局部变量意味着using namespace std将使您无法使用count而不是std::count

不想要的名称冲突的经典示例如下所示。想象一下您是一个初学者,对std::count一无所知。假设您正在<algorithm>中使用其他功能,或者它被看似无关的标头拉入。带有一些长嵌套的类型。

没关系,因为std::count进入了全局名称空间,而函数计数将其隐藏了。也许有些令人惊讶,这还可以。导入到声明性作用域中的标识符出现在公共命名空间中,该命名空间同时包含了定义它们的位置和导入它们的位置。换句话说,std::count在全局命名空间中显示为std::count,但仅在count内部可见。

#include <algorithm>
using namespace std;

int count = 0;

int increment()
{
    return ++count; // error, identifier count is ambiguous
}


并且出于类似的原因,increment在这里是不明确的。 count不会引起using namespace std,请隐藏外部std::countcount规则意味着using namespace看起来(在std::count函数中)看起来像是在全局范围内声明的,即在与increment相同的范围内声明,因此引起了歧义。

评论


但是如果没有std ::前缀,它的键入非常容易!

–xtofl
09年8月12日在9:57

@xtofl:不,不是。键入时五个字符无关紧要,但是阅读时这五个字符可能非常相关。易于阅读比源代码易于键入更为重要,因为代码的可读性比编写的要多。

–sbi
09年8月12日10:00

您可以添加using语句与范围规则一起正确运行。

–马丁·约克
09年8月12日在14:40

@Martin York:更新了带有示例作用域规则的示例。 @Michael Burr:可以说这还算不错,我真正不喜欢的是简单错误的错误信息很难解释,或者根本不发生。例如,如果一个函数被认为在范围之内,但不是,而std ::函数在,而不是得到一个有用的“识别器未识别”错误,那么您通常会遇到一个更加晦涩的“无法转换参数” X”或“无法从模板生成函数”样式错误。更糟糕的是,如果错误地调用了一个错误的函数。很少见,但确实如此。

– CB Bailey
09年8月12日在21:47

好吧,很惊讶没有人讨论使用std :: xxx;的选项。它不会造成名称空间污染,编写代码会更短,而且我认为copy的可读性比std :: copy大得多。

–legends2k
2010-2-18在23:43



#2 楼

不包括基础知识(必须在所有stl对象/函数之前添加std ::,如果没有“使用命名空间std”,则冲突的可能性较小)

还值得注意的是,您应该永远不要将

using namespace std

放置在头文件中,因为即使它们不想使用该名称空间,它也可以传播到包含该头文件的所有文件。 br />
在某些情况下,使用诸如

using std::swap


很有好处,就像有特殊版本的swap一样,编译器会使用,否则它将退回到std::swap

如果调用std::swap,则始终使用基本版本,而不会调用优化版本(如果存在)。

评论


使用std :: swap(这是我唯一使用过的东西)来提及+1。

–sbi
09年8月12日在9:47

+1表示u n可以传播。只是要注意,它也可能会侵入正确构造的标头中:它们只必须包含在流氓标头之后。

– Qumrana
09年9月5日在17:09

但是,如果要定义交换或移动(或哈希,较少等)专业化,则无论如何都应将该专业化放入命名空间std中。例如:名称空间std {template <>类hash {public:size_t operator()(const X&)const};} class X:{friend size_t std :: hash :: operator()(const X& )};

– AJMansfield
2015年11月30日17:30



#3 楼

首先,使用一些术语:



使用声明:using std::vector;


使用指令:using namespace std;


我认为使用using指令很好,只要在头文件的全局作用域中不使用它们即可。因此,在.cpp文件中包含

using namespace std;


并不是一个真正的问题,而且事实证明,它完全在您的控制之下(甚至可能是如果需要,则适用于特定的块)。我看不出有什么特别的理由来用大量的std::限定词来弄乱代码-它只是一堆视觉噪声。但是,如果您没有在代码中使用来自std命名空间的全部名称,那么我也看不到省略该指令的问题。这是一种重言式-如果该指令不是必需的,则无需使用它。

同样,如果您可以在std命名空间中使用一些用于特定类型的using-声明(而不是using-directives),那么就没有理由不应该只带那些特殊的名称到当前名称空间中。出于同样的原因,我认为这很疯狂,而且当单个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指令之后在实现文件中自由地使用声明和指令使用命名空间,并对此感到满意。尽管反复声明相反,使用声明和指令的名称空间并不是邪恶的,它们不会破坏名称空间的目的。相反,它们是使命名空间可用的原因。

在“ C ++编程语言,第三版”中经常引用Stroupstrup说:“不要污染全局命名空间”。实际上,他确实说过(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()。没有赋予全局名称
优先于在全局范围内可访问的名称空间中的名称

这提供了重要的保护
,防止意外的名称冲突,并且–
重要的是–确保
污染全局名称空间不会带来任何好处。

当声明许多名称的库
通过
使用-指令,这是一个显着的优点
,未使用的名称冲突不会被视为错误。

...

我希望看到一个基本的与传统的C和C ++程序相比,在使用命名空间的新
程序中减少了全局名称的使用。
命名空间的规则是专门设计的,
不会使
懒惰的全局名称用户优于那些不污染的人。全局范围。


又如何与“全局名称的懒用户”具有相同的优势?通过利用using伪指令,可以安全地使名称空间中的名称可用于当前作用域。 using指令(通过将指令放置在std之后)不会污染全局名称空间。只是使这些名称易于使用,并提供持续的冲突保护。

评论


关于您的最后一点:Java和C#也具有更整洁的名称空间。如果BCL中的所有内容都驻留在System中,则“使用System”将引起与“使用命名空间std”一样多的麻烦。

–杰夫·哈迪(Jeff Hardy)
09年8月12日在16:58

但是,我看到的Java和C#程序通常会引入它们使用的所有名称空间-而不仅仅是“系统”(或其等效名称)。因此,有5个或10个执行或多或少的相同功能,而不是引入所有已使用名称的单个using指令。另外,是否“使用名称空间标准”;真的造成那么多麻烦吗?

– Michael Burr
09年8月12日在17:31

问题在于std的通用名称过多,而包含一个标准头的标准名称可能包括所有其他名称。我们对进口的商品没有很好的控制,存在太多的风险。我对Java和C#的了解还不够,但是我对Ada的了解却比C ++更好,并且模块输入系统通常不受欢迎。通常,首先是命名约定(我见过人们使用前缀和名称空间,不导入没有意义),然后才是样式。

– AProgrammer
09年8月12日在17:38

我仍然不相信这是一个现实问题。我看到使用指令一直使用,没有严重的缺点。再说一次,不使用它们我没有问题。我只是更喜欢std ::限定词而不会使代码混乱—还有其他方法可以避免这种情况(使用声明或typedef通常可以解决问题)。

– Michael Burr
09年8月12日在18:03

@AProgrammer:您说的是,“ list是用于在lisp解释器中标识列表的自然标识符”-但具有“使用命名空间std”;指令不会阻止您声明自然标识符'list'-只是如果您这样做,就不能再使用std :: list而不对其进行限定。这与没有“使用命名空间标准”的情况没有什么不同。指示。还是我错过了什么?

– Michael Burr
09年8月12日在20:22

#4 楼

切勿在头文件的全局范围内使用命名空间。这可能会导致冲突,并且出现冲突的文件负责人无法控制原因。
在实现文件中,选择的内容要少得多。 />
放置使用名称空间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;



#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的方式修改“模块”,因此可以使用“引擎”库而无需重新编译其用户。集成测试还可以。新的“模块”发布。引擎的下一次编译在未修改其代码时中断。

评论


我认为使用名称空间是可以接受的受控情况之一是在发布代码中。简化可以简化页面的布局,并有助于将精力集中在暴露点上。缺点是它实际上并没有表现出很好的做法,因此我不会在初学者的书中使用它。

– AProgrammer
09年8月12日在9:24

我认为输入std ::是为了澄清而付出的很小的代价

– paoloricardo
09年8月12日上午10:13

@paoloricardo:另一方面,我认为std ::到处显示都是不必要的视觉混乱。

– Michael Burr
09年8月12日在16:46

@Michael:您付钱,您就做出选择!

– paoloricardo
09年8月12日在19:41

感谢您抽出宝贵时间添加遇到的问题的详细信息。

– Michael Burr
09年8月13日在16:28

#5 楼

如果您不存在与标准库和其他库的代码中名称冲突的风险,则可以使用:

文档代码或存在名称冲突的风险,请使用其他方法:在代码中使用可以为您带来更大的安全性,但也许会使代码有些沉重...

#6 楼



using std::string;




using namespace std;


向全局名称空间添加了一些符号(一个或多个)。将符号添加到全局名称空间是您永远不应在头文件中执行的操作。您无法控制谁将包含您的标头,有很多标头包含其他标头(以及标头包含标头的标头等等)。

在实现(.cpp)文件中,此操作由您决定(只记得在所有#include指令之后执行此操作)。您只能破坏此特定文件中的代码,因此更易于管理和找出名称冲突的原因。如果您更喜欢在标识符前使用std::(或任何其他前缀,则项目中可能有很多名称空间),可以。如果您想将用于全局标识符的标识符添加到全局名称空间,就可以了。如果您想把整个名称空间放在脑子里:-),这取决于您。虽然效果仅限于单个编译单元,但是可以接受。

#7 楼

对我来说,我更喜欢在可能的情况下使用::

std::list<int> iList;


我不想写:

希望使用C ++ 0x,我可以这样写:

for(std::list<int>::iterator i = iList.begin(); i != iList.end(); i++)
{
    //
}


如果命名空间很长,


评论


@AraK:名称空间dir = boost :: filesystem;我猜这是一个别名吗?

– paoloricardo
09年8月12日在10:17

@paoloricardo:是的,这就是它。

–sbi
09年8月12日13:49

迭代器应该使用++ i而不是i ++进行递增,因为即使定义了迭代器,它也会创建不必要的迭代器临时副本。

– Felix Dombek
11年7月16日在8:16

#8 楼

您永远不要在标头中的名称空间范围内使用using namespace std。另外,我想大多数程序员会怀疑他们何时看到不带vectorstringstd::,所以我认为using namespace std更好。因此,我认为绝对不会存在。

如果您觉得必须,请使用using namespace std之类的声明添加本地。但是问自己:这有什么价值?一行代码只写一次(也许两次),但是却被读取了十,百或千次。与读取代码相比,通过添加using声明或指令节省的键入工作量很小。

考虑到这一点,十年前的一个项目中,我们决定使用所有名称空间的完整名称来明确限定所有标识符。起初看起来很尴尬的事情在两周内就变成了常规。现在,在整个公司的所有项目中,没有人再使用指令或声明了。 (除了一个例外,请参阅下文。)十年后的代码(几个MLoC)让我觉得我们做出了正确的决定。

我发现通常,那些反对禁止using std::vector的人通常没有为一个项目尝试过。那些尝试过的人通常会发现,比在很短的时间内使用指令/声明更好。

注意:唯一的例外是using,它是必需的(尤其是在通用代码中),以拾取无法放入using std::swap名称空间的swap()的重载(因为我们不允许放置std函数进入此命名空间)。

评论


std :: swap的专业化将是一个完整的专业化-您不能对功能模板进行部分专业化。任何程序都可以部分规范任何标准库模板,只要该规范取决于用户定义的类型即可。

– CB Bailey
09年8月12日在10:54

@Charles:是的,您是对的,当然,没有FTPS。而且我可以在std中专门化模板,但不能重载。对不起,这个脑袋。我会改正这个帖子。

–sbi
2009年8月12日13:45

我不认为using namespace指令的目的是要进行输入。相反,这是为了使阅读更容易,因为正如您所说,该代码将必须被读取数十,数百或数千次。对于某些人来说,std ::杂乱无章,读起来容易得多。但这可能归结为个人的感知能力;有些人过滤std ::离开甚至需要它作为指导(例如衬线),其他人踩踏它并感觉就像在崎y不平的道路上。

–卢米
2011-12-27 12:36

@Lumi:无论您喜欢较短还是较长的散文都是主观的,但是前缀可以客观地增加代码的清晰度。

–sbi
2011-12-27 12:56

@sbi:不,那不是客观的。这取决于您是否认为std ::是有用的还是混乱的。更加混乱->不够清晰。

–约书亚·理查森(Joshua Richardson)
2013年9月4日在17:40

#9 楼

命名空间保留包含代码,以防止混淆和污染函数签名。 :

#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();
}


#10 楼

using namespace std导入当前名称空间中std命名空间的内容。因此,这样做的好处是您不必在该名称空间的所有函数前面键入std::。但是,可能会发生具有具有相同名称功能的不同名称空间的情况。因此,您可能会结束不调用所需的调用的操作。

std中手动指定要导入的操作可以防止这种情况的发生,但是可能会导致在文件开头使用的列表很长。 ,有些开发人员会发现它很丑陋;)!个人而言,我个人宁愿每次使用函数时都指定名称空间,除非名称空间过长,在这种情况下,我会在编辑文件的开头。

编辑:如另一个答案中所述,切勿将using namespace放入标头文件中,因为它会传播到包括该标头的所有文件,因此可能会产生不良行为。 br />
EDIT2:由于Charles的评论,更正了我的答案。

评论


使用命名空间std;将std名称空间的内容导入全局名称空间。它不会更改默认名称空间。在使用命名空间std之后在全局命名空间中定义某些内容不会神奇地将其放入std命名空间。

– CB Bailey
09年8月12日在9:14

抱歉,这不是我的意思。感谢您指出来,我将更正我的答案。

–Wookai
09年8月12日在9:26

伙计们:感谢您的答复。看来,通常来说,不使用“使用命名空间std”并避免产生潜在的歧义会更安全。总而言之,使用'std :: xxx'比在源文件的开头声明各种功能的列表更吸引我,因为它明确地表明了您的意图。

– paoloricardo
09年8月12日在9:41

Quote(除非名称空间太长)。您可以使用名称空间别名来提供帮助。 '命名空间Rv1 = Thor :: XML :: XPath :: Rules :: Light :: Version1;'注意别名并使用两个服从范围规则;

–马丁·约克
09年8月12日在14:47

#11 楼

就像在Java中,您可以使用可以包含java.util。*或简单地单独选择每个类一样,这取决于样式。请注意,您不希望在文件/广域范围的开始处出现一个using namespace std,因为您会污染名称空间并可能发生冲突,从而破坏了名称空间。但是,如果您有一个使用大量STL的函数,则会使代码混乱,从而使逻辑中的前缀语法变得混乱,您可能应该考虑使用using namespace std(使用各种类时)或单独使用using(使用时)经常上几堂课。

#12 楼

只要您使用的IDE不够灵活,无法显示或隐藏所需的确切信息,此讨论就将继续进行。

这是因为您希望代码的外观取决于

创建源代码时,我更喜欢确切地看到我使用的是哪个类:是std::string还是BuzFlox::Obs::string类?

设计时在控制流中,我什至对变量的类型都不感兴趣,但我想重点关注ifwhilecontinue

所以这是我的建议:

根据代码的读者和工具的功能,选择最容易阅读或提供最多信息的方式。

#13 楼

有几种方法可以解决此问题。

第一:像您一样使用。

第二:执行namespace S = std;,减少2个字符。

第三:请使用static

第四:请勿使用std所使用的名称。

#14 楼

除了这里的许多正确答案外,我想说明一个经常被遗忘的细节:由于通用的编码和链接方案,许多开发人员似乎误解了C ++中的翻译单元的概念。对于我们大多数人来说,这很常见,.h文件是头文件,.cpp文件会产生实现细节,但这只是同意,而不是严格的标准规则。最终如何组合翻译单元将取决于您自己。
因此,例如,使用UnityBuilds(将所有内容链接到一个翻译单元以提高速度和存储目的),您就不再可以依靠共同的同意,在.cpp文件中使用命名空间通常是可以的。有人可能会争辩说,UnityBuilds绕过了几种主要的C ++核心哲学,因此它们本身就是一个主题,但是尽管如此,在其他情况下仍可能出现类似的问题(例如,函数中包含本地)。顺便说一下,匿名命名空间的使用也出现了类似的问题。
由于过去这是我的问题,因此我想出了“内部准则”,以便在比例允许的情况下尽可能明确它。

#15 楼


每个优点和缺点的分别是什么?


离开std ::的唯一理由是,从理论上讲,您可以自己重新实现所有STL功能。然后您的功能可以从使用std :: vector切换到my :: vector,而无需更改代码。

评论


命名空间并不是真正设计为允许使用不同但等效的功能替换名称。它们旨在防止意外的名称冲突。

– Michael Burr
09年8月12日在17:35

是的,因此打破此用法的“ using”指令的唯一理由是允许您将函数切换到新的名称空间。

–马丁·贝克特(Martin Beckett)
09年8月12日在17:48

我认为您会发现很多程序员抱怨ass命名空间有什么痛苦,并且如果没有使用指令,他们希望将它们扔到窗外。据我所知,每种使用名称空间的语言都具有与using指令相似的功能,以便在您希望使用它们时避免使用它们。如果指令无用,为什么它们无处不在?

– Michael Burr
09年8月12日在19:11

我认为“使用”旨在让您切换到其他实现,而不是保存键入的3个字母。我喜欢使用“ std :: Foo”,因为它是我使用普通Foo的程序员合同,他们不必检查。我同意我不想键入“ com.microsoft.visual-studio.standard-library.numbers.int foo”,STL中的某些迭代器声明就是这样。 Python做得很好,它允许您从模块中引入经过修饰或未经修饰的函数集。

–马丁·贝克特(Martin Beckett)
09年8月12日在19:30

#16 楼

为什么不例如

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>&);


哪个是返回值?