我听说过使用名称空间std;这是不好的做法,我应该直接使用std::cout和std::cin。这是为什么?声明与std命名空间中的某个变量同名的变量是否有风险?


当前回答

这可能比Greg写的更糟!

Library Foo 2.0可能会引入一个函数Qux(),它无疑比多年来代码调用的bar::Qux)更适合您对Qux的某些调用。然后你的代码仍然可以编译,但它会默默地调用错误的函数,上帝知道会发生什么。这是最糟糕的事情了。

请记住,std名称空间有大量的标识符,其中许多是非常常见的标识符(例如列表、排序、字符串、迭代器等),这些标识符也很可能出现在其他代码中。

如果你认为这不太可能:在我给出这个答案大约半年后,在Stack Overflow上有一个问题(由于省略了std::前缀而调用了错误的函数)。这是这一问题的另一个最近的例子。所以这是一个真正的问题。


这里还有一个数据点:很多很多年前,我还发现必须在标准库中的所有内容前面加上std::,这很烦人。然后我在一个项目中工作,一开始就决定禁止使用指令和声明,但函数作用域除外。猜猜怎么了?我们中的大多数人花了几周的时间才习惯写前缀,再过几周,我们中的大部分人甚至同意这实际上使代码更可读。这是有原因的:你喜欢较短或较长的散文是主观的,但前缀客观上增加了代码的清晰度。不仅是编译器,您也会发现更容易看到引用的是哪个标识符。

在十年内,该项目发展到拥有数百万行代码。由于这些讨论一次又一次出现,我一度很好奇项目中实际使用(允许)函数范围的频率。我查阅了它的来源,只找到了一两打使用它的地方。对我来说,这表明,一旦尝试过,即使在允许使用std::的地方,开发人员也不会觉得每100kLoC使用一次指令就足够痛苦。


一句话:明确地给每件事加上前缀不会造成任何伤害,只需要很少的时间就可以习惯,而且有客观的优势。特别是,它使编译器和人类读者更容易解释代码,这可能是编写代码时的主要目标。

其他回答

这可能比Greg写的更糟!

Library Foo 2.0可能会引入一个函数Qux(),它无疑比多年来代码调用的bar::Qux)更适合您对Qux的某些调用。然后你的代码仍然可以编译,但它会默默地调用错误的函数,上帝知道会发生什么。这是最糟糕的事情了。

请记住,std名称空间有大量的标识符,其中许多是非常常见的标识符(例如列表、排序、字符串、迭代器等),这些标识符也很可能出现在其他代码中。

如果你认为这不太可能:在我给出这个答案大约半年后,在Stack Overflow上有一个问题(由于省略了std::前缀而调用了错误的函数)。这是这一问题的另一个最近的例子。所以这是一个真正的问题。


这里还有一个数据点:很多很多年前,我还发现必须在标准库中的所有内容前面加上std::,这很烦人。然后我在一个项目中工作,一开始就决定禁止使用指令和声明,但函数作用域除外。猜猜怎么了?我们中的大多数人花了几周的时间才习惯写前缀,再过几周,我们中的大部分人甚至同意这实际上使代码更可读。这是有原因的:你喜欢较短或较长的散文是主观的,但前缀客观上增加了代码的清晰度。不仅是编译器,您也会发现更容易看到引用的是哪个标识符。

在十年内,该项目发展到拥有数百万行代码。由于这些讨论一次又一次出现,我一度很好奇项目中实际使用(允许)函数范围的频率。我查阅了它的来源,只找到了一两打使用它的地方。对我来说,这表明,一旦尝试过,即使在允许使用std::的地方,开发人员也不会觉得每100kLoC使用一次指令就足够痛苦。


一句话:明确地给每件事加上前缀不会造成任何伤害,只需要很少的时间就可以习惯,而且有客观的优势。特别是,它使编译器和人类读者更容易解释代码,这可能是编写代码时的主要目标。

我同意其他人的看法——这是在要求名字冲突、歧义,但事实是它不够明确。虽然我可以看到使用的用途,但我个人倾向于限制它。我也会强烈考虑其他人所指出的:

如果你想找到一个可能是一个相当常见的名称的函数名,但你只想在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(但这是一个很好的练习,也是让自己学会另一种语言的好方法)。不要对对象/类等抱有偏见,这可能更好地表述为不那么封闭、不那么傲慢、更容易接受。)。但有用的是一些人已经提出的建议:我确实使用了列表(它相当通用,不是吗?),并排序(相同的事情)以命名两个,如果我使用名称空间std;,因此,为了达到这个目的,我更喜欢具体、可控,并且知道如果我打算将其作为标准用途,那么我必须指定它。简单地说:不允许假设。

至于让Boost的正则表达式成为标准的一部分。我这样做是为了将来的集成,而且——再次,我完全承认这是偏见——我不认为它像Boost::正则表达式::。。。。事实上,这对我来说是另一回事。C++中有很多东西我还没有完全接受,包括外观和方法(另一个例子:varadic模板与var参数[尽管我承认varadic模版非常有用!])。即使那些我确实接受的人也很困难,我仍然对他们有意见。

我不认为这在任何情况下都是不好的做法,但在使用它时都需要小心。如果你正在编写一个库,你可能应该在命名空间中使用作用域解析运算符,以防止你的库与其他库冲突。对于应用程序级代码,我看不出它有什么问题。

如果您导入了右侧的头文件,您的全局范围中会突然出现十六进制、左侧、加号或count等名称。如果您不知道std::包含这些名称,这可能会令人惊讶。如果您也尝试在本地使用这些名称,可能会导致一些混淆。

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

它不会使您的软件或项目性能更差。在源代码开头包含名称空间还不错。using namespace std指令的包含根据您的需求以及开发软件或项目的方式而有所不同。

命名空间std包含C++标准函数和变量。当您经常使用C++标准函数时,这个名称空间非常有用。

如本页所述:使用命名空间std的语句通常被认为是错误的实践此语句的替代方法是指定使用作用域运算符(::)将标识符所属的命名空间每次我们声明一个类型。请参阅以下意见:在源文件中使用“using namespace std”没有问题当您大量使用命名空间并确信任何东西都不会碰撞。

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

如本页所述:尽管该语句使我们不必在任何时候输入std::我们希望访问std命名空间中定义的类或类型将整个std命名空间导入当前命名空间该计划的一部分。让我们举几个例子来理解为什么可能不是什么好事...现在,在开发的后期阶段,我们希望使用cout是在某个名为“foo”的库中自定义实现的(用于示例)...注意这里有一个歧义,cout指向哪个库?编译器可能会检测到这一点,而不会编译程序。在最坏的情况下在这种情况下,程序仍可能编译,但调用了错误的函数,因为我们从未指定标识符属于哪个命名空间。