使用标准命名空间


110

关于std名称空间使用“ using”似乎有不同的看法。

有些人说使用' using namespace std,其他std::人说不使用,而是给将与' ' 一起使用的std函数添加前缀,而其他人则说使用类似以下内容:

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

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

各自的优缺点是什么?




Answers:


131

大多数C ++用户都非常乐于阅读std::stringstd::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::countcount在全局命名空间中一样可见,但仅在内部可见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
}

21
但它的类型SOOOO没有的std ::前缀容易得多!
xtofl,2009年

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

3
您可以添加using语句与范围规则一起正确运行。
马丁·约克

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

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

41

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

还值得注意的是,您永远不要放

using namespace std

在头文件中,因为它可以传播到包含该头文件的所有文件,即使它们不想使用该名称空间也是如此。

在某些情况下,使用诸如

using std::swap

好像有一个特殊版本的swap一样,编译器将使用该版本,否则将依赖于std::swap

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


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

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

1
但是,如果要定义swapmove(或hashless等等)专业化,则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&)};
AJMansfield

28

首先,一些术语:

  • using-declarationusing std::vector;
  • 使用指令using namespace std;

我认为,只要不在头文件的全局范围内使用using指令,就可以使用using指令。所以有

using namespace std;

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

同样,如果您可以在命名空间中使用一些用于特定类型的using- 声明(而不是using-directivesstd,那么就没有理由不应该将那些特定的名称带入当前命名空间中。出于同样的原因,我认为这样做很疯狂,而且当单个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)可用于范围的名称空间中的名称不会污染全局名称空间。只是使这些名称易于使用,并提供持续的冲突保护。


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

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

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

1
我仍然不相信这是一个现实问题。我看到使用指令一直都在使用,而没有严重的缺点。再说一次,使用它们我没有问题。我只是希望std::限定符不会使代码混乱不堪-还有其他方法可以避免这种情况(使用声明或typedef通常可以解决问题)。
迈克尔·伯

1
@AProgrammer:您说的是,“ list是用于在lisp解释器中标识列表的自然标识符”-但是拥有“ using namespace std;”指令并不会阻止您声明自然标识符' list'-只是如果这样做,您就不能长时间使用std::list而不合格。这与没有“ using namespace std;”指令没有什么不同。还是我错过了什么?
迈克尔·伯

17

切勿在头文件的全局范围内使用命名空间。这可能导致冲突,并且出现冲突的文件负责人无法控制原因。

在实现文件中,选择的余地要少得多。

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


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

1
我认为输入std ::是为提高清晰度付出的代价很小
paoloricardo

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

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

2
感谢您抽出宝贵时间来添加遇到的问题的详细信息。
迈克尔·伯

4

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

using namespace std;

但是,如果您想确切地知道代码对文档的依赖性,或者存在名称冲突的风险,请使用另一种方法:

using std::string;
using std::cout;

第三种解决方案,不要使用这些解决方案并编写std ::,因为每次使用代码都可以提高安全性,但是可能会使代码有些沉重...


4

using std::string;

using namespace std;

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

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


3

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

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;
}

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

@paoloricardo:是的,这就是它。
2009年

2
迭代器应使用递增++i,这不是i++因为即使定义了迭代器也不会创建不必要的临时副本。
菲利克斯·唐贝克

2

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

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

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

我发现通常情况下,那些反对禁令的人using通常不会在一个项目中尝试过。那些尝试过的人通常会发现,比在很短的时间内使用指令/声明更好。

注意:唯一的例外是using std::swap必要的(特别是在通用代码中),以拾取swap()不能放入std命名空间的重载(因为不允许我们将std函数的重载放入此命名空间)。


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

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

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


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

2

命名空间包含代码,以防止功能签名的混淆污染

正确 命名空间用法的完整且有文档证明的演示:

#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

1

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

手动指定要导入的文件std可以防止这种情况的发生,但是可能会导致在文件开头使用一长串文件,有些开发人员会发现这很丑陋;)!

就个人而言,我更喜欢每次使用函数时都指定名称空间,除非名称空间过长(在这种情况下,我会在文件的开头放置一些use)。

编辑:如另一个答案中所述,您永远不要using namespace在标头文件中放置,因为它会传播到包括该标头的所有文件,因此可能会产生不良行为。

EDIT2:更正了我的答案,这要感谢Charles的评论。


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

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

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

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

0

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


0

只要您使用的IDE不够灵活,就无法显示或隐藏所需的确切信息,那么此讨论将一直活跃。

那是因为您希望代码看起来像是取决于手头的任务。

在创建源代码时,我更喜欢确切地看到正在使用std::stringBuzFlox::Obs::string类:是还是类?

在设计控制流时,我什至对变量的类型都不感兴趣,但我希望重点关注if“和while”和continue“ 和”。

所以这是我的建议:

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


0

有几种解决方法。

第一:像你一样使用。

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

第三:使用static

第四:不要使用使用的名称std


-1

各自的优缺点是什么

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


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

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

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

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

-1

为什么不举个例子

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&); 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.