使用std命名空间

pao*_*rdo 105 c++ namespaces

关于std命名空间使用'using'似乎有不同的看法.

有人说使用' using namespace std',其他人说不要,而是先加上与' std::' 一起使用的std函数,而其他人则说使用这样的东西:

using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::vector;
Run Code Online (Sandbox Code Playgroud)

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

各自的优点和缺点是什么?

CB *_*ley 127

大多数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
}
Run Code Online (Sandbox Code Playgroud)

该错误通常很长且不友好,因为它std::count是一个具有一些长嵌套类型的模板.

这是可以的,因为std::count进入全局命名空间并且函数计数隐藏它.

#include <algorithm>
using namespace std;

int increment()
{
    static int count = 0;
    return ++count;
}
Run Code Online (Sandbox Code Playgroud)

也许有点令人惊讶,这是可以的.导入到声明性作用域中的标识符出现在公共名称空间中,该名称空间包含它们的定义位置和导入位置.换句话说,在全局命名空间中std::count可见count,但仅在内部increment.

#include <algorithm>

int increment()
{
    using namespace std;
    static int count = 0;
    return ++count;
}
Run Code Online (Sandbox Code Playgroud)

由于类似的原因,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
}
Run Code Online (Sandbox Code Playgroud)

  • @xtofl:不,它没有.键入时五个字符不相关,但这五个字符在阅读时可能非常相关.阅读的简易性远比简单的源代码输入更重要,因为代码读取比写入更多. (69认同)
  • 但是如果没有std ::前缀,它会更容易输入_soooo_! (21认同)
  • 好吧,我很惊讶没有人讨论过`使用std :: xxx;`的选项.它没有做命名空间污染,编写代码会更短,我认为`copy`比`std :: copy`更重要. (5认同)
  • 您可以添加using语句与范围规则正确运行. (3认同)
  • @Martin York:更新了说明范围规则的示例.@Michael Burr:可以说这并不是那么糟糕,我真正不喜欢的是简单错误的错误信息难以解释,或者根本没有发生.例如,如果一个函数被认为是在范围内,但是不是并且std :: function是,而不是得到一个有用的'标识符无法识别'错误,你经常会得到一个更加模糊的'无法转换参数X'或'无法从模板生成函数'样式错误.更糟糕的是,如果一个错误的函数被静默调用.这很罕见,但却发生了. (2认同)
  • 作为实验,我编写了一个名为“等于”的函数,该函数花了三倍。然后,我使用有效参数调用它,但故意将其拼写为“ equal”。没有`uns;`,我收到一条简单的消息“在此范围内未声明'equal'”。有了它,我收到了关于2个错误的27行消息,例如“'double'不是类,结构或联合类型”,而'__simple'不是类型'bool'的有效模板参数,因为它不是-恒定表达”。经验丰富的C ++开发人员可能不会费力地通过它来查找根本原因,但是大多数初学者都会感到困惑。(gcc 4.4.0) (2认同)
  • 值得注意的是,如果你有这个:`int swap; using namespace std; int main(){:: swap = 1; 它始终是非模糊的:明确指定范围将搜索使用指令放置的命名空间 - 但它不会导致与现有声明冲突.这仅在非限定名称查找时发生 (2认同)

Yac*_*oby 40

排除基础知识(必须添加所有stl对象/函数的std :: infront,如果没有'using namespace std',则冲突的可能性更小)

值得注意的是,你永远不应该放

using namespace std
Run Code Online (Sandbox Code Playgroud)

在头文件中,因为它可以传播到包含该头文件的所有文件,即使他们不想使用该命名空间.

在某些情况下,使用像这样的东西是非常有益的

using std::swap
Run Code Online (Sandbox Code Playgroud)

好像有一个专门的swap版本,编译器将使用它,否则它将回退到std :: swap

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

  • +1提到`使用std :: swap`(这是我唯一使用过的东西). (10认同)
  • +1 提到“uns”可以传播。只是要注意,它也可以蠕虫进入正确构造的标头:它们只需包含在恶意标头之后即可。 (2认同)

Mic*_*urr 27

首先,一些术语:

  • 使用声明: using std::vector;
  • using-directive: using namespace std;

我认为使用using-directives很好,只要它们不在头文件的全局范围内使用.所以有

using namespace std;
Run Code Online (Sandbox Code Playgroud)

在你的.cpp文件中并不是一个真正的问题,如果事实证明,它完全在你的控制之下(如果需要它甚至可以限定为特定的块).我没有看到任何特定的理由用一大堆std::限定符来混淆代码- 它只是变成了一堆视觉噪音.但是,如果您没有std在代码中使用命名空间中的一大堆名称,我也会发现删除指令没有问题.这是一个重言式 - 如果指令不是必要的,那么就没有必要使用它了.

类似地,如果您可以在命名空间中使用一些using声明(而不是using-directive)来表示特定类型std,那么就没有理由不应该只将那些特定的名称带入当前的命名空间.出于同样的原因,我认为当一个使用指令也可以做到这一点时,拥有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>()

    // ...
 }
Run Code Online (Sandbox Code Playgroud)

我认为我们应该看看各种语言的常用习语,它们大量使用命名空间.例如,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声明增加了一个名字,以局部范围.一个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
}
Run Code Online (Sandbox Code Playgroud)

本地声明的名称(由普通声明或using声明声明)隐藏了同名的非本地声明,并且在声明时检测到名称的任何非法过载.

注意k++in 的歧义错误f1().全局名称不会优先于全局范围内可访问的名称空间中的名称.这为意外名称冲突提供了重要保护,并且 - 重要的是 - 确保从污染全局命名空间中获得的任何优势都没有.

当通过using-directives使可以访问声明许多名称的库时,未使用的名称的冲突不被视为错误是一个重要的优点.

...

我希望与传统的C和C++程序相比,使用命名空间的新程序中全局名称的使用会大幅减少.命名空间的规则是专门设计的,对于那些注意不要污染全局范围的人而言,对全球名称的"懒惰"用户没有任何好处.

人们如何与"全球名称的懒惰用户"具有相同的优势?通过利用using-directive,它可以安全地使名称空间中的名称可用于当前作用域.

请注意,有一个区别 - std命名空间中的名称可以通过正确使用using-directive(通过在其后放置指令#includes)使范围可用,不会污染全局命名空间.它只是简单地提供这些名称,并持续防止冲突.


APr*_*mer 17

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

在实现文件中,选择远没有那么好.

  • 使用using namespace std会带来该命名空间中的所有符号.这可能是麻烦的,因为几乎没有人知道那里的所有符号(因此在实践中不可能有冲突的政策)而没有说出将要添加的符号.并且C++标准允许头部添加来自其他头部的符号(C不允许这样做).它仍然可以在实践中很好地简化在受控情况下的书写.如果发生错误,则会在文件中检测到有问题.

  • 使用std :: name; 具有写作简单的优点,没有导入未知符号的风险.成本是您必须明确导入所有想要的符号.

  • 显然有资格添加一点点混乱,但我认为这是一些练习较少的麻烦.

在我的项目中,我对所有名称使用显式限定,我接受使用std :: name,我反对使用命名空间std(我们有一个lisp解释器,它有自己的列表类型,所以冲突是肯定的事情).

对于其他名称空间,您还必须考虑使用的命名约定.我知道一个项目使用命名空间(用于版本控制)和名称前缀.做一个using namespace X然后几乎没有风险,不这样做导致愚蠢的代码PrefixNS::pfxMyFunction(...).

在某些情况下,您要导入符号.std :: swap是最常见的情况:导入std :: swap然后使用swap unqualified.参数依赖查找将在类型的命名空间中找到适当的交换(如果有)并且如果没有则返回到标准模板.


编辑:

在评论中,Michael Burr想知道冲突是否发生在现实世界中.这是一个真实的例子.我们有一种扩展语言,是一种lisp方言.我们的解释器有一个包含文件,包含lisp.h

typedef struct list {} list;
Run Code Online (Sandbox Code Playgroud)

我们必须集成并调整一些代码(我将其命名为"engine"),如下所示:

#include <list>
...
using std::list;
...
void foo(list const&) {}
Run Code Online (Sandbox Code Playgroud)

所以我们修改如下:

#include <list>

#include "module.h"
...
using std::list;
...
void foo(list const&) {}
Run Code Online (Sandbox Code Playgroud)

好.一切正常.几个月后,"module.h"被修改为包含"list.h".测试通过了."模块"没有以影响其ABI的方式进行修改,因此可以使用"引擎"库而无需重新编译其用户.集成测试没问题.新的"模块"出版.当代码未被修改时,下一次引擎编译就破了.

  • @paoloricardo:另一方面,我认为让std ::遍布这个地方是不必要的视觉混乱. (4认同)
  • 感谢您抽出宝贵时间添加遇到的问题的详细信息。 (2认同)

归档时间:

查看次数:

87302 次

最近记录:

7 年,3 月 前