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


当前回答

我认为本地或全球使用应取决于应用程序。

因为,当我们在本地使用库时,有时代码会变得一团糟。可读性将下降。

因此,只有在可能发生冲突时,我们才应该在本地使用库。

我不是一个更有经验的人。所以,如果我错了,请告诉我。

其他回答

短版本:不要在头文件中使用全局using声明或指令。可以在实现文件中使用它们。以下是Herb Sutter和Andrei Alexandrescu在C++编码标准中对这个问题的看法(我用粗体强调):

总结命名空间using是为了方便您,而不是为了强加给他人:不要在#include指令之前编写using声明或using指令。推论:在头文件中,不要使用指令或声明编写命名空间级别;相反,显式命名空间限定所有名称。(第二条规则与第一条规则相同,因为标头永远无法知道后面可能出现的其他标头#includes。)讨论简而言之:在#include指令之后,你可以也应该在你的实现文件中自由地使用命名空间使用声明和指令,并对此感到满意。尽管重复了相反的断言,但使用命名空间的声明和指令并不是邪恶的,它们不会破坏命名空间的目的。相反,它们使名称空间可用。

我同意这里的其他观点,但我想解决有关可读性的问题——您可以通过在文件、函数或类声明的顶部使用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;
}

or:

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

为什么使用命名空间标准?

C++有一个标准库,其中包含构建应用程序时使用的常用功能,如容器、算法等。如果这些库使用的名称是公开的,例如,如果它们全局定义了一个队列类,那么您将无法再次使用相同的名称而不发生冲突。因此,他们创建了一个名称空间std来包含此更改。

不使用的原因1:不良行为

使用命名空间std的语句通常被认为是错误的做法。此语句的另一种方法是在每次声明类型时使用作用域运算符(::)指定标识符所属的命名空间。尽管该语句避免了我们在任何时候希望访问std命名空间中定义的类或类型时键入std::,但它将整个std命名空间导入到程序的当前命名空间中。

不使用的原因2:编译器变得混乱

在玩具程序中导入整个std库是可以的,但在生产级代码中,这是不好的。使用命名空间std;使命名空间std中声明的每个符号都可以在没有命名空间限定符的情况下访问。

例如:

现在,假设您升级到一个较新版本的C++,并将更多新的std命名空间符号注入到您不知道的程序中。您的程序中可能已经使用了这些符号。现在编译器将很难弄清楚声明的符号是属于您自己的实现,还是来自您在没有任何概念的情况下导入的名称空间。一些编译器会抛出错误。如果运气不好,编译器选择了错误的实现并编译它,这肯定会导致运行时崩溃。

命名空间污染影响:

虽然这种做法对于示例代码来说是可以的,但将整个std命名空间拉入全局命名空间是不好的,因为它会破坏命名空间的目的,并可能导致名称冲突。这种情况称为命名空间污染。

有一个非常简单的答案:这是防御性编程。您知道,使用名称空间std;-可以使std::size_t、std::cout等的使用更容易一些我希望您不必相信这样的指令在标题中没有位置!然而,在翻译单元中,您可能会受到诱惑。。。

作为std命名空间一部分的类型、类等随着每次C++修订而增加。如果放松std::限定符,可能会有太多的歧义。放松std中经常使用的名称的限定符是完全合理的,例如,使用std::fprintf;,或者更可能的是:使用std::size_t;-但是,除非这些已经是语言的很好理解的部分(或者特别是C库的std包装),否则只使用限定符。

当您可以使用typedef并结合自动和decltype推理时,从可读性/可维护性的角度来看,实际上没有什么好处。

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

如果你想找到一个可能是一个相当常见的名称的函数名,但你只想在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模版非常有用!])。即使那些我确实接受的人也很困难,我仍然对他们有意见。