提问



其他人告诉我,在代码中写using namespace std是错误的,我应该直接使用std::coutstd::cin


为什么using namespace std被认为是一种不好的做法?它是否效率低或是否有风险声明模糊变量(与std命名空间中的函数具有相同名称的变量)?它会影响性能吗?

最佳参考


这根本与性能无关。但请考虑一下:您正在使用两个名为Foo和Bar的库:


using namespace foo;
using namespace bar;


一切正常,你可以从Foo调用Blah()而从Bar调用Quux()没有问题。但有一天你升级到新版本的Foo 2.0,它现在提供了一个名为Quux()的功能。现在你遇到了冲突:Foo 2.0和Bar都将Quux()导入你的全局命名空间。这需要花一些时间来修复,特别是如果函数参数碰巧匹配的话。


如果您使用foo::Blah()bar::Quux(),则foo::Quux()的引入将是非事件。

其它参考1


我同意格雷格所写的一切,但我想补充一句: 它甚至可能比格雷格说的更糟糕!


Library Foo 2.0可以引入一个函数Quux(),这对你Quux()的一些调用比你的代码多年来调用的bar::Quux()更明确地匹配。然后你的 代码仍会编译 ,但是 它会默默地调用错误的函数 ,并且天知道什么。那就像事情一样糟糕。


请记住,std命名空间有大量标识符,其中许多是非常常用标识符(想listsortstringiterator等等,也很可能出现在其他代码中。


如果你认为这不太可能:在我给出这个答案大约半年之后,在Stack Overflow上有一个问题,其中几乎发生了这种情况(错误的函数被称为由于省略了std::前缀)。这是另一个最近这样一个问题的例子。
所以这是一个真正的问题。





这是另外一个数据点:许多年前,我也常常发现它不得不使用std::标准库中的所有内容作为前缀。然后我在一个项目中工作,在开始时决定两个using指令和声明都被禁止,除了功能范围。猜猜是什么?我们大部分时间都花了很长时间才习惯编写前缀,经过几周之后,我们大多数人甚至同意它实际制作了代码更具可读性。有一个理由: 无论你喜欢更短或更长的散文都是主观的,但前缀客观上增加了代码的清晰度。 不仅是编译器,而且您也更容易看到引用了哪个标识符。


十年来,该项目增长了数百万行代码。由于这些讨论一次又一次地出现,我曾经很奇怪(允许的)功能范围using实际上在项目中的使用频率。我发现它的来源并且只发现了它的一两个地方。对我而言,这表明,一旦尝试过,开发人员发现std::痛苦到足以使用指令甚至每100个kLoC甚至允许使用它。





底线:明确地为所有内容添加前缀不会造成任何伤害,很少习惯,并且具有客观优势。特别是,它使代码更容易被编译器和人类读者解释 - 这应该是主要的编写代码时的目标。

其它参考2


我认为把它放在你的类的头文件中是很糟糕的:因为那样你就会强迫任何想要使用你的类(通过包含你的头文件)来使用(即看到所有内容)那些其他名称空间。


但是,您可以随意在(私有)* .cpp文件中放置using语句。





请注意,有些人不同意我的说法感觉自由,因为尽管cpp文件中的using语句更好而不是标题(因为它不会影响包含标题的人文件),他们认为它仍然不是好(因为根据代码,它可能使类的实现更难维护)。这个FAQ主题说,[127]



  using-directive存在于遗留C ++代码中,并且可以简化向命名空间的转换,但您可能不应该定期使用它,至少不应该在新的C ++代码中使用它。



它提出了两种选择:



  • 使用声明:


    using std::cout; // a using-declaration lets you use cout without qualification
    cout << "Values:";
    

  • 克服它并输入std ::


    std::cout << "Values:";
    


其它参考3


我最近遇到了关于Visual  Studio  2010的投诉。事实证明,几乎所有源文件都有这两行:[128]


using namespace std;
using namespace boost;


很多Boost功能都进入了C ++ 0x标准,而Visual  Studio 2010具有很多C ++ 0x功能,所以突然这些程序没有编译。[129]


因此,避免using namespace X;是一种面向未来的形式,这种方法可以确保对正在使用的库和/或头文件的更改不会破坏程序。

其它参考4


简短版本:不要在头文件中使用全局使用声明或指令。随意在实现文件中使用它们。这就是Herb Sutter和Andrei Alexandrescu在C ++编码标准中对此问题的看法(重点是我的):



  

摘要


  
  命名空间使用是为了您的方便,而不是让您对其他人造成:在#include指令之前,切勿编写using声明或using指令。

  
  推论:在头文件中,不要使用指令或使用声明来编写命名空间级别;相反,显式命名空间限定所有名称。 (第二条规则从第一条开始,因为标题永远不会知道其他标题#includes可能会出现在它们之后。)

  
  

讨论


  
  简而言之:在#include指令之后,你可以而且应该在你的实现文件中使用声明和指令来使用命名空间,并且感觉良好。 尽管反复断言相反,使用声明和指令的命名空间并不是邪恶的,并且它们不会破坏命名空间的目的。相反,它们是使命名空间可用的原因


其它参考5


不应该在全局范围内使用using指令,尤其是在头文件中。但是在某些情况下,即使在头文件中也是如此:


template <typename FloatType> inline
FloatType compute_something(FloatType x)
{
    using namespace std; //no problem since scope is limited
    return exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4));
}


这比明确的资格要好(std::sinstd::cos ...)
因为它更短并且能够使用用户定义的浮点类型(通过Argument Dependent Lookup)。

其它参考6


不要全局使用



只有当全球使用时才会被视为糟糕。因为:



  • 你弄乱了你正在编程的命名空间。

  • 当您使用多个using namespace xyz时,读者很难看到特定标识符的来源。

  • 对于其他的源代码读者来说,对于最常见的读者来说更是如此:你自己。一年或两年后回来看看......

  • 如果你只谈论using namespace std你可能不知道你抓到的所有东西 - 当你添加另一个#include或转移到新的C ++版本时,你可能会得到名字冲突不知道。



您可以在本地使用



继续在本地(几乎)自由使用它。当然,这可以防止你重复std:: - 重复也很糟糕。


本地使用它的习惯用法



在C ++ 03中,有一个成语 - 样板代码 - 用于为类实现swap函数。有人建议您实际使用本地using namespace std - 或至少using std::swap:


class Thing {
    int    value_;
    Child  child_;
public:
    // ...
    friend void swap(Thing &a, Thing &b);
};
void swap(Thing &a, Thing &b) {
    using namespace std;      // make `std::swap` available
    // swap all members
    swap(a.value_, b.value_); // `std::stwap(int, int)`
    swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
}


这有以下魔力:



  • 编译器将为value_选择std::swap,即void std::swap(int, int)

  • 如果你实现了重载void swap(Child&, Child&),编译器会选择它。

  • 如果不有重载,编译器将使用void std::swap(Child&,Child&)并尽量交换这些。



使用C ++ 11,没有理由再使用这种模式。 std::swap的实施被改变以发现潜在的过载并选择它。

其它参考7


如果导入正确的头文件,则在全局范围内突然出现hexleftpluscount等名称。如果您不知道std::包含这些名称,这可能会令人惊讶。如果你也尝试在本地使用这些名称,可能会引起一些混乱。[131] [132] [133] [134]


如果所有标准内容都在其自己的命名空间中,则不必担心与您的代码或其他库的名称冲突。

其它参考8


有经验的程序员使用任何解决问题的方法,避免出现任何问题,并且出于这个原因,他们避免使用头文件级别的使用指令。


有经验的程序员也会尽量避免在源文件中对名称进行完全限定。造成这种情况的一个小原因是,当代码较少时,编写更多代码并不优雅除非有充分的理由。这样做的一个主要原因是关闭依赖于参数的查找(ADL)。


这些好的理由是什么?有时程序员明确想要关闭ADL,有时他们想要消除歧义。


所以以下都可以:



  1. 函数implements
  2. 中的函数级using-directives和using-declarations
  3. 源文件中的源文件级使用声明

  4. (有时)源文件级使用指令


其它参考9


我同意它不应该全局使用,但在本地使用并不是那么邪恶,就像在namespace中一样。这里是C ++编程语言的一个例子 :


namespace My_lib {

    using namespace His_lib; // everything from His_lib
    using namespace Her_lib; // everything from Her_lib

    using His_lib::String; // resolve potential clash in favor of His_lib
    using Her_lib::Vector; // resolve potential clash in favor of Her_lib

}


在这个例子中,我们解决了潜在的名称冲突和由其组成引起的歧义。


在那里显式声明的名称(包括由His_lib::String等using声明声明的名称)优先于using-directive(using namespace Her_lib)在另一个作用域中可访问的名称。

其它参考10


另一个原因是惊喜。


如果我看到cout << blah,而不是std::cout << blah


我想这是什么cout?这是正常的cout吗?这是特别的吗?

其它参考11


我也认为这是一种不好的做法。为什么?只有一天,我认为命名空间的功能是划分东西,所以我不应该把所有东西扔到一个全球包中来破坏它。
但是,如果我经常使用cout和cin,我在cpp文件中写道:using std::cout; using std::cin;(永远不会在#include传播的头文件中)。我认为没有人会说出一个流coutcin。 ;)

其它参考12


很高兴看到代码并知道它的作用。如果我看到std::cout我就知道那是std库的cout流。如果我看到cout那么我就不知道了。可能是std库的cout流。或者可能有int cout = 0;在同一个函数中高出10行。或者在该文件中名为coutstatic变量。它可以是任何东西。


现在需要一百万行代码库,这不是特别大,而且你正在寻找一个bug,这意味着你知道这一百万行中有一行不能完成它应该做的事情。[[cout << 1;可以读取名为coutstatic int,将其向左移一位,然后扔掉结果。寻找一个错误,我必须检查一下。你能看到我真的更喜欢看std::cout吗?


如果你是一名教师而且从来没有必要编写和维护任何代码,那么这些东西似乎是一个非常好的主意。我喜欢看到代码,其中(1)我知道它的作用;以及,(2)我相信写作的人知道它的作用。

其它参考13


这完全取决于管理复杂性。使用命名空间会把你不想要的东西拉进去,因此可能会让调试变得更难(我说可能)。在整个地方使用std ::更难阅读(更多文字和所有内容)。


课程的马匹 - 管理你的复杂性,你最好能和最好的能力。

其它参考14



  1. 您需要能够阅读与您有不同风格和最佳实践意见的人所编写的代码。

  2. 如果你只是使用cout,没有人会感到困惑。但是当你有很多名字空间飞来飞去而你看到这个类并且你不确定它做什么时,明确命名空间就像一个评论的排序。乍一看,你可以看到哦,这是一个文件系统操作或那就是做网络的东西。


其它参考15


同时使用许多命名空间显然是一种灾难,但是在我看来,使用JUST命名空间std和命名空间std并不是什么大不了的事,因为重新定义只能通过你自己的代码来实现...


所以只需将它们的功能视为保留名称,如int或class,就是这样。


人们应该停止这么肛门。你的老师一直都是对的。只需使用ONE命名空间;这是首先使用命名空间的重点。您不应该同时使用多个。除非是你自己的。因此,重新定义不会发生。

其它参考16


考虑


// myHeader.h
#include <sstream>
using namespace std;


// someoneElses.cpp/h
#include "myHeader.h"

class stringstream {  // uh oh
};


请注意,这是一个简单的示例,如果您有包含20个包含和其他导入的文件,那么您将有大量的依赖关系来解决问题。更糟糕的是,您可以在其他模块中获得无关的错误取决于冲突的定义。


这并不可怕,但是你不会在头文件或全局命名空间中使用它来节省头痛。在非常有限的范围内完成它可能是正常的,但我从来没有遇到过输入额外的5个字符以澄清我的功能来自何处的问题。

其它参考17


一个澄清问题的具体例子。想象一下,你有一个情况,你有2个库,foo和bar,每个库都有自己的命名空间:


namespace foo {
    void a(float) { /* does something */ }
}

namespace bar {
    ...
}


现在让我们假设您在自己的程序中使用foo和bar,如下所示:


using namespace foo;
using namespace bar;

void main() {
    a(42);
}


在这一点上一切都很好。当你运行你的程序时,它做了一些事情。但是后来你更新吧,让我们说它已经变成了:


namespace bar {
    void a(float) { /* does something completely different */ }
}


此时您将收到编译器错误:


using namespace foo;
using namespace bar;

void main() {
    a(42);  // error: call to 'a' is ambiguous, should be foo::a(42)
}


所以你需要做一些维护来澄清你的意思(即foo::a)。这可能是不可取的,但幸运的是它很容易(只需在所有人面前添加foo::调用a,编译器标记为不明确的)。


但想象一下另一种方案,其中bar改为看起来像这样:


namespace bar {
    void a(int) { /* does something completely different */ }
}


此时你对a(42)的调用突然与bar::a而不是foo::a结合,而不是做某事,而是完全不同。没有编译器警告或任何东西你的程序只是默默地开始做一些与以前完全不同的事情。


当您使用命名空间时,您会冒这样的情况,这就是人们使用命名空间感到不舒服的原因。命名空间中的事情越多,冲突的风险就越大,因此人们使用命名空间std可能会更加不舒服(由于该命名空间中的事物数量比其他命名空间。


最终,这是可写性与可靠性/可维护性之间的权衡。可读性也可以考虑,但我可以看到这种论点的论点。通常我会说可靠性和可维护性更重要,但在这种情况下,您将不断支付可写性成本以获得相当罕见的可靠性/可维护性影响。最佳权衡取决于您的项目和优先级。

其它参考18


命名空间是命名范围。命名空间用于对相关声明进行分组并保持分离
物品分开。例如,两个单独开发的库可以使用相同的名称来表示不同的名称
项目,但用户仍然可以使用两者:


namespace Mylib{
    template<class T> class Stack{ /* ... */ };
    / / ...
}
namespace Yourlib{
    class Stack{ /* ... */ };
    / / ...
}
void f(int max) {
    Mylib: :Stack<int> s1(max) ; / / use my stack
    Yourlib: :Stack s2(max) ; / / use your stack
    / / ...
}


重复命名空间名称可能会分散读者和作者的注意力。因此,这是可能的
声明在没有明确限定的情况下可以使用特定命名空间中的名称。例如:


void f(int max) {
    using namespace Mylib; / / make names from Mylib accessible
    Stack<int> s1(max) ; / / use my stack
    Yourlib: :Stack s2(max) ; / / use your stack
    / / ...
}


命名空间为管理不同的库和不同版本的库提供了强大的工具
    码。特别是,它们为程序员提供了如何明确地引用非本地语言的替代方案
    名称。


来源:C ++编程语言概述
作者:Bjarne Stroustrup

其它参考19


我同意这里的其他人,但是想解决有关可读性的问题 - 你可以通过在文件,函数或类声明的顶部使用typedef来避免所有这些问题。


我通常在我的类声明中使用它,因为类中的方法倾向于处理类似的数据类型(成员),而typedef是指定在类的上下文中有意义的名称的机会。这实际上有助于类方法定义的可读性。


//header
class File
{
   typedef std::vector<std::string> Lines;
   Lines ReadLines();
}


并在实施中:


//cpp
Lines File::ReadLines()
{
    Lines lines;
    //get them...
    return lines;
}


而不是:


//cpp
vector<string> File::ReadLines()
{
    vector<string> lines;
    //get them...
    return lines;
}


要么:


//cpp
std::vector<std::string> File::ReadLines()
{
    std::vector<std::string> lines;
    //get them...
    return lines;
}

其它参考20


使用命名空间std的示例由于计数的模糊性而引发了编译错误,这也是算法库中的函数。


#include <iostream>

using namespace std;

int count = 1;
int main() {
    cout<<count<<endl;
}

其它参考21


在所有条件下,我认为这不一定是不好的做法,但是在使用它时需要小心。如果你正在编写一个库,你可能应该使用命名空间的范围解析运算符来保持你的库与其他库的对接。对于应用程序级代码,我没有看到它有什么问题。

其它参考22


为什么使用命名空间std;在C ++中被认为是一种不好的做法?


我把它反过来说:为什么输入5个额外的字符被一些人认为是麻烦的?


考虑例如写一个数字软件,当向量是问题领域最重要的概念之一时,为什么我会考虑通过将一般的std :: vector切换为向量来污染我的全局命名空间?

其它参考23


使用非限定导入的标识符,您需要外部搜索工具,如 grep ,以找出声明标识符的位置。这使得关于程序正确性的推理更加困难。

其它参考24


为了回答你的问题,我实际上以这种方式看待它:许多程序员(不是全部)调用命名空间std。因此,人们应该养成不使用与命名空间std中的名称相同或使用相同名称的东西。这是一个很大的问题,但与严格来说可能提出的可能连贯的单词和假名的数量相比,并没有那么多。


我的意思是真的......说不要依赖于这种存在只是让你依赖它不存在。你经常会有问题借用代码片段并不断修复它们。只要保持你的用户 - 定义和借用的东西应该是有限范围内的,并且非常保留全局变量(诚然,全局变量应该是现在编译,后来理智的最后手段。)我认为这是你老师的坏建议,因为使用std将同时适用于cout和std :: cout,但不使用std只能用于std :: cout。你不会总是有幸编写所有自己的代码。


注意:在你真正了解编译器的工作原理之前,不要过分关注效率问题。通过编写一些经验,在你意识到他们能够概括出好的代码之前,你不必了解它们。做一些简单的事情。每一点都很简单,好像你用C编写了整个东西。好的代码只是它需要的复杂。

其它参考25


这取决于它的位置。如果它是一个公共头,那么你通过将它合并到全局命名空间来减少命名空间的值。请记住,这可能是制作模块全局变量的一种巧妙方法。

其它参考26


根据我的经验,如果您有多个库使用say,cout,但出于不同的目的,您可能会使用错误的cout


例如,如果我输入,using namespace std;using namespace otherlib;并输入cout(恰好在两者中),而不是std::cout(或'otherlib::cout'),你可能使用错误的,并得到错误,使用std::cout会更加有效和高效。

其它参考27


钍是一种不好的做法,通常被称为全局名称空间污染。当多个名称空间具有相同的带有签名的函数名称时,可能会出现问题,然后编译器决定调用哪个名称将是不明确的,当您使用函数调用指定名称空间时,这一切都可以避免std::cout]]。希望这可以帮助。 :)

其它参考28


我同意其他人的意见 - 它要求名称冲突,含糊不清,然后事实是它不那么明确。虽然我可以看到using的使用,但我个人的偏好是限制它。我也会强烈考虑其他人指出的内容:


如果你想找到一个可能是一个相当普通名称的函数名,但你只想在std命名空间中找到它(或者反过来 - 你想要改变所有不在命名空间中的调用std]],命名空间X,...),那你怎么建议这样做?你可以编写一个程序来做这件事,但是花时间在你的项目上工作而不是编写一个程序来维护你的项目会更好吗?


就个人而言,我实际上不介意std::前缀。我喜欢看起来而不是没有它。我不知道那是不是因为它是明确的并且对我说这不是我的代码..我正在使用标准库或者如果它是其他东西,但我觉得它看起来更好。这可能很奇怪,因为我最近才进入C ++(使用并且仍然使用C和其他语言更长时间而C是我的最喜欢的语言,正好在汇编之上)。


还有一件事虽然它与上述有些相关,但其他人指出了什么。虽然这可能是不好的做法,但我有时会为标准库版本和名称保留std::name以用于特定于程序的实现。是的,这可能会咬你并咬你,但这一切都归结为我从头开始这个项目而且我是唯一的程序员。例如:我超载std::string并称之为string我有一些有用的补充。我之所以这样做是因为我的C和Unix(+ Linux)倾向于小写名称。


除此之外,您还可以拥有名称空间别名。以下是一个可能未被引用的示例。我使用C ++ 11标准,特别是libstdc ++。好吧,它没有完整的std::regex支持。当然它会编译,但它会抛出异常,因为它是程序员的错误。但缺乏实施。所以这就是我如何解决它。安装Boost的正则表达式,将其链接进去。然后,我执行以下操作,以便当libstdc ++完全实现它时,我只需删除此块并且代码保持不变:


namespace std
{
    using boost::regex;
    using boost::regex_error;
    using boost::regex_replace;
    using boost::regex_search;
    using boost::regex_match;
    using boost::smatch;
    namespace regex_constants = boost::regex_constants;  
}


我不会争论这是否是一个坏主意。但我会争辩说,它保持它对我的项目干净,同时使其具体:真的我必须使用Boost但我使用它就像libstdc ++最终将拥有它。是的,开始您自己的项目并从一开始就使用标准(...)开始,这有助于维护,开发以及项目涉及的所有内容!


点击
现在我有时间,只是澄清一些事情。我实际上并不认为在故意使用STL中的类/任何内容的名称是更好的,更具体地代替。字符串是例外(忽略第一个,上面或第二个,如果你是双关语对我来说,因为我不喜欢字符串的想法。事实上,我仍然偏向于C并且偏向于C ++。保留细节,我工作的大部分内容更适合C(但这是一个很好的练习和一个很好的方法,使自己成为一个。学习另一种语言和b。尽量不要偏向于对象/类/等等,这可能是更好的陈述不那么封闭,不那么傲慢,更容易接受。但是有用的是一些已经提出的建议:我确实使用了list(它是相当通用的,不是吗?),排序(同样的事情)来命名两个如果我这样做会导致名称冲突using namespace std;]]所以为此我更喜欢具体,控制和知道,如果我打算将它作为标准用途,那么我将不得不指定它。简单地说:不假设允许。


至于将Boost的正则表达式作为std的一部分。我这样做是为了未来的整合 - 再次,我完全承认这是偏见 - 我不认为它像boost::regex:: ...那样丑陋]]对我来说是另一回事。在C ++中有许多东西我还没有完全接受外观和方法(另一个例子:可变参数模板与var args [[尽管我承认可变参数模板非常有用!]])。即使那些我接受它的人也很困难,我仍然遇到问题。

其它参考29


它不会使您的软件或项目性能变差,在源代码开头包含命名空间并不是很糟糕。包含using namespace std指令会根据您的需要以及您开发软件或项目的方式而有所不同。


namespace std包含C ++标准函数和变量。当您经常使用C ++标准函数时,此命名空间很有用。



  如本页所述:[135]

  
  使用命名空间std的语句通常被认为是错误的
  实践。此声明的替代方法是指定
  标识符所属的命名空间使用范围运算符(::)
  每次我们声明一个类型。

  
  并看到这个意见:[136]

  
  在源文件中使用using namespace std没有问题
  当你大量使用命名空间并确定这一点时
  没有什么会碰撞。



有些人说在你的源文件中包含using namespace std是一种不好的做法,因为你从该命名空间调用了所有的函数和变量。当你想要定义一个与另一个函数同名的新函数时namespace std中包含的函数会使函数重载,并且由于编译或执行会产生问题。它不会像你期望的那样编译或执行。



  如本页所述:[137]

  
  虽然该语句使我们无法输入std :: when
  我们希望访问std命名空间中定义的类或类型
  将整个std命名空间导入当前命名空间
  该计划。让我们举几个例子来理解为什么会这样
  可能不是一件好事

  
  ...

  
  现在处于开发的后期阶段,我们希望使用另一个版本
  在一些名为foo的库中自定义实现的cout(for
  例)

  
  ...

  
  请注意cout指向哪个库有歧义?
  编译器可能会检测到这一点,而不是编译程序。在最坏的情况下
  例如,程序可能仍然编译但调用错误的函数,因为
  我们从未指定标识符属于哪个命名空间。