Using std Namespace

There seem to be different views on using 'using' with respect to the std namespace.

Some say use ' using namespace std', other say don't but rather prefix std functions that are to be used with ' std::' whilst others say use something like this:

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

for all the std functions that are to be used.

What are the pros and cons of each?

116551 次浏览

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

同样值得注意的是,你永远不应该把

using namespace std

In a header file, as it can propagate to all files that include that header file, even if they don't want to use that namespace.

在某些情况下,使用

using std::swap

如果有一个特殊版本的交换,编译器将使用它,否则它将回到 std::swap

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

using namespace std导入当前名称空间中的 std内容。因此,这样做的好处是不必在该名称空间的所有函数前面键入 std::。但是,可能会出现具有相同名称的函数的不同命名空间。因此,您可能不会打电话给您想要的那个。

std中手动指定要导入哪些内容可以防止这种情况发生,但是可能会导致在文件开头处出现一个长长的使用列表,某些开发人员会觉得这很难看;)!

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

编辑: 正如在另一个答案中指出的那样,您永远不应该将 using namespace放在头文件中,因为它将传播到包括此头文件在内的所有文件,从而可能产生不必要的行为。

编辑2: 更正我的答案,感谢查尔斯的评论。

就像在 Java 中一样,你可以使用任何一个都可以包含 Java.util。* 或单独选择每个类,这取决于风格。请注意,您不希望在 file/wide 作用域的开始部分有一个 using namespace std,因为这样会污染名称空间,并可能产生冲突,从而破坏名称空间的作用点。但是如果你有一个使用大量 STL 的函数,它会使代码杂乱无章,在你的逻辑中有一堆混乱的前缀语法,你可能应该考虑使用 using namespace std(当使用各种类时)或者单独的 using(当经常使用一些类时)。

大多数 C + + 用户在阅读 std::stringstd::vector等内容时都非常高兴。事实上,看到一个原始的 vector让我怀疑这是 std::vector还是一个不同的用户定义的 vector

我一直反对使用 using namespace std;。它将各种名称导入全局名称空间,并可能导致各种不明显的歧义。

下面是 std名称空间中的一些常见标识符: count、 sort、 find、 equals、 return。有一个称为 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;
}

Perhaps slightly surprisingly, this is OK. Identifiers imported into a declarative scope appear in the common namespace that encloses both where they are defined and where they are imported into. In other words, std::count is visible as count in the global namespace, but only inside 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
}

如果您的代码中没有与 std 或其他库发生名称冲突的风险,您可以使用:

using namespace std;

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

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

第三种解决方案是,不要使用这些解决方案,而是在代码中每次使用之前编写 std: : ,这会给您带来更多的安全性,但是,代码中可能会有一点沉重..。

不要在头文件的全局范围内使用命名空间。这可能导致冲突,而冲突发生地的档案负责人无法控制冲突的起因。

在实现文件中,选择远没有那么精确。

  • Putting a using namespace std brings all the symbols from that namespaces. This can be troublesome as nearly no body know all the symbols which are there (so having a policy of no conflict is impossible to apply in practice) without speaking of the symbols which will be added. And the C++ standard allows an header to add symbols from other headers (the C one doesn't allow that). It still can work well in practice to simplify the writing in controlled case. And if an error occur, it is detected in the file which has the problem.

  • 使用 std: : name; 具有编写简单的优点,而无需导入未知符号的风险。代价是您必须显式地导入所有需要的符号。

  • 明确限定添加了一点杂乱,但我认为这是少麻烦的一些实践。

在我的项目中,我对所有名称都使用明确的限定,我接受使用 std: : name,我打 against using namespace std (we have an lisp interpreter which has his own list type and so conflict is a sure thing).

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

在某些情况下,您需要导入这些符号。交换是最常见的情况: 您导入 std: : swap,然后使用未限定的交换。依赖于参数的查找将在类型的命名空间中找到一个适当的交换区(如果有的话) ,如果没有的话,则返回到标准模板。


编辑:

在评论中,迈克尔 · 伯尔怀疑这些冲突是否发生在现实世界中。这是一个真实的例子。我们有一种扩展语言,它是一种 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 的方式进行修改,因此可以使用“引擎”库而无需重新编译其用户。集成测试正常。新「模组」出版。引擎的下一次编译在其代码未被修改时中断。

都有

using std::string;

还有

using namespace std;

向全局名称空间添加一些符号(一个或多个)。向全局名称空间添加符号是 永远不会在头文件中应该做的事情。您无法控制谁将包括您的头,有许多头包括其他头(和头包括头包括头,等等...)。

实施时(。Cpp)文件,这取决于您(只要记住这样做 之后所有 # include 指令)。您只能破解这个特定文件中的代码,因此更容易管理和找出名称冲突的原因。如果您喜欢在标识符之前使用 std: : (或任何其他前缀,您的项目中可能有许多名称空间) ,那么可以。如果您希望将您使用的标识符添加到全局名称空间,那么可以。如果您想把整个名称空间放在您的头上: ——) ,这取决于您。虽然效果仅限于单个编译单元,但它是可以接受的。

只要您使用的 IDE 不够灵活,不足以显示或隐藏您需要的确切信息,这个讨论就会继续下去。

这是因为您希望代码的外观取决于手头的任务。

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

When designing the control flow, I'm not even interested in the types of the variables, but I want a focus on if's and while's and continue's.

所以我的建议是:

根据代码的受众和工具的强大功能,选择最容易阅读或提供大部分信息的方式。

对我来说,如果可能的话,我更喜欢使用 ::

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。此外,我认为大多数程序员会想知道,当他们看到 vectorstring没有 std::,所以我认为不是 using namespace std更好。因此,我主张永远不要成为 using namespace std

如果您觉得必须这样做,可以使用像 using std::vector这样的声明添加局部声明。但问问你自己: 这值多少钱?一行代码只写了一次(也许是两次) ,但是它被读取了10次、100次或者1000次。与阅读代码的工作相比,添加 using 声明或指令所节省的输入工作是微不足道的。

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

我发现通常,那些反对禁止 using的人通常没有在一个项目中尝试过。那些尝试过的人通常会发现,这种方法在很短的时间内就比使用指令/声明要好。

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

首先,一些术语:

  • 使用-声明 : using std::vector;
  • Using- 指令 : using namespace std;

I think that using 使用指令 are fine, as long as they aren't used at the global scope in a header file. So having

using namespace std;

in your .cpp file isn't really a problem, and if it turns out to be, it's completely under your control (and it can even be scoped to particular blocks if desired). I see no particlar reason to clutter up the code with a slew of std:: qualifiers - it just becomes a bunch of visual noise. However, if you're not using a whole bunch of names from the std namespace in your code, I also see no problem with leaving out the directive. It's a tautology - if the directive isn't necessary, then there's no need to use it.

类似地,如果对于 std名称空间中的特定类型,可以使用一些 使用-声明(而不是 使用指令) ,那么没有理由不将这些特定名称引入当前名称空间。出于同样的原因,我认为有25个或30个 using-声明是很疯狂的,而一个 using-command 也可以达到同样的效果,这会带来簿记上的麻烦。

记住,有时 必须的使用 using-声明也是有好处的。请参考 Scott Meyers 的《第25项: 考虑对非抛出交换的支持》(Item 25: Think support for a non-throw swap) ,选自有效的 C + + ,第三版。为了让泛型的、模板化的函数使用参数化类型的“最佳”交换方法,您需要使用 using-Declaration 和参数依赖性查找(又名 ADL 或 Koenig lookup) :

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-direct。这不会导致大范围的问题,而且很少有问题是在“例外”的基础上通过完全限定名或别名处理问题名称——就像在 C + + 中可以做到的那样。

Herb Sutter 和他的 Andrei Alexandrescu 们在他们的著作《 C + + 编码标准: 101规则、指南和最佳实践:

简而言之: 在 #include指令之后,您可以并且应该在实现文件中大量使用声明和指令来使用名称空间,并且对此感觉良好。尽管反复声明相反,但是使用声明和指令的名称空间并不邪恶,它们也不会破坏名称空间的作用。相反,它们是使名称空间可用的因素。

Stroupstrup is often quoted as saying, "Don’t pollute the global namespace", in "The C++ Programming Language, Third Edition". He does in fact say that (C.14[15]), but refers to chapter C.10.1 where he says:

使用-声明将名称添加到 本地范围。一个 using-directive做 not; it simply renders names 它们在其中的范围内可以访问 例如:

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
}

本地声明的名称(声明为 通过普通声明或 隐藏非局部 declarations of the same name, and any 名称的非法重载是 在声明点检测到。

k++的歧义错误 不给出全局名称 优先于名称空间中的名称 在全局范围内进行访问。 这提供了重要的保护 防止意外的名字冲突,以及- 重要的是-确保有 无利可图 污染全局命名空间。

当库声明许多名称时 are made accessible through 使用指令,这是一个重要的 未使用名称冲突的优势 不被认为是错误。

...

我希望看到 the use of global names in new 使用命名空间的程序与 传统的 C 和 C + + 程序 命名空间的规则是专门的 crafted to give no advantages to a 全局名称的“惰性”用户 someone who takes care not to pollute 全球范围。

那么,如何才能拥有与“懒惰的全局名称用户”相同的优势呢?通过利用 using-direct,安全使命名空间中的名称对当前作用域可用。

请注意,这里有一个区别——通过正确使用 using 指令(将指令放在 #includes之后) ,使得范围可以使用 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&);

命名空间保持代码包含以防止函数签名的 混乱污染

这里 适当的 命名空间使用方法的一个完整且有记录的演示:

#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

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

第一: 像你做的那样使用。

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

第三: 使用 static

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

除了这里的许多正确答案之外,我想说明一个经常被遗忘的细节: 由于常见的编码和链接方案,C + + 中的翻译单元的概念似乎被许多开发人员误解了。这对我们大多数人来说都很正常。H 文件是头文件。Cpp 文件产生了实现细节,但这只是一个同意,而不是标准的严格规则。你如何最终复合你的翻译单位取决于你的疑问。

因此,例如 UnityBuilds (将所有内容链接到一个单独的翻译单元以提高速度和内存) ,您不能再依赖于在。一般来说,cpp 文件是很好的。有些人可能会争辩说,UnityBuilds 绕过了几个主要的 C + + 核心理念,因此它们本身就是一个主题,但是还有更多类似问题可能发生的场景(例如,函数中的本地包含)。顺便提一下,使用匿名名称空间也会出现类似的问题。

由于这在过去对我来说是一个问题,我提出了“内部指导方针”,使事情尽可能明确,只要比例允许。