C++名称解析

7
我对C++中的namespace和using有些疑惑,我想知道它们之间的区别,并找出最佳使用方法。
据我所见,至少有三种方法可以解决类名称,我不确定应该如何在它们之间进行选择:
1. using namespace 2. using :: 3. :: 我想知道其中的优势,特别是是否涉及性能问题,如果只是语法上的偏好或者其他我没有考虑到的事情。

1
它与运行时性能无关。 - Alex F
1
我在大学时被告知,除了将所有命名空间引入作用域之外,使用 using namespace std; 也是一个安全漏洞。 - Adri C.S.
1
  1. 基于您所描述的原因,这是不被鼓励的。
  2. 使用ADL(参见std::swap,但最好让答案更好地解释)可以帮助您在自己的命名空间中使用其他人提供的函数。
  3. 我最喜欢的是这种方式,但这是个人口味问题,如果您不像我一样喜欢在代码中到处看到std::,那么可能会很繁琐。但它们之间没有任何性能差异,它们只涉及名称解析,并且不会“编译成实际代码”。
- Christian Rau
5个回答

6
首先是一个using namespace指令,它将指定命名空间中的所有符号名称引入到当前命名空间中,无论您是否需要或使用它们。显然这是不可取的。
其次是using namespace声明。它只会将指定的符号名称引入到当前命名空间中。优点是您不必每次都输入完全限定的名称。
第三种方法是符号的完全限定名称。缺点是你必须在使用符号时到处输入完全限定的名称。
显然,第二和第三种方法更合适。它们之间没有性能差异。唯一的区别是你要输入的字符数。简单地说,根据你的编码标准选择其中之一即可。
编辑: 正如@Jerry所指出的那样,将using声明与ADL(参数相关查找)结合使用可能会导致不良影响。 您可以在我的一个答案中找到详细的解释: 详细解释Koenig查找如何与命名空间配合工作以及为什么是好事? 在部分中, 为什么批评Koenig算法?

我有点喜欢使用命名空间声明,因为我可以把它放在我的 .cpp 文件中,以便我能看到“这些命名空间在这个文件中被使用”的列表。但是,我也有点喜欢完全限定的名称,因为一旦我看到它们,它们就会告诉我这个功能来自于这个命名空间。但好吧,我想我正在以某种混合的方式做这件事,在得到的答案中,我感到很放心能够继续下去。感谢您花时间解释这个问题。 - qrikko
1
通常情况下,人们会遵循组织的编码标准。否则,这是个人选择。不过重要的是一旦做出选择就要保持一致性。 - Alok Save
@qrikko:很遗憾,如果你仔细阅读我的回答,你会意识到以下两点:1)你的安全性并不像你想象的那样高;2)所有声称对性能没有任何影响的答案(间接地)至少有可能是错误的。他们说的形式并不直接影响性能,这一点是正确的,但他们说(甚至暗示)性能不会受到影响是错误的。 - Jerry Coffin

5

有一种情况(尽管比较少见)在使用的形式确实会有所不同,你需要使用的形式是using namespace foo,并且最常用于std命名空间(即在其中写using namespace std;)。

最明显的例子是,你正在为用户定义的类型编写排序算法。有可能会应用于用户还定义了自己的swap的类型。

你遇到了这样的情况:如果用户定义了swap,则想使用他们的swap,但如果他们没有定义,则希望使用std::swap。如果直接在代码中使用std::swap,那么即使该类型有其自己定义的交换函数,仍将使用std::swap。相反,如果直接指定了特定类型的交换函数,并且未提供函数,则你的代码将无法编译。

为了解决这个问题,可以采取以下措施:

using namespace std;

template <class Iter>
void my_sort(Iter first, Iter last) {
    // ...
    if (*last < *first)
        swap(*first, *last);
}

这将会为特定于正在比较的类型的交换找到一个(即,在与该类型相同的命名空间中定义的swap),如果有的话(通过参数依赖查找),并且对于该类型未定义任何一个,它将使用std::swap(通过using namespace std;)。
这可能会影响性能 - 如果他们为其类型编写了一个专门的交换,您通常可以预期这是因为这样做,他们可以提供更好的性能。这意味着明确指定std::swap可能有效,但可能会导致较差的性能。
否则,这几乎完全是方便和可读性的问题 - 我大多数情况下喜欢给出全名(例如std::swap),除非在上面的情况下(在我编写代码的时候),至少有两种可能性可能更受欢迎,我想给编译器足够的自由来选择正确的方法。
我发现使用声明/指令的另一个有用的时间是当命名空间被嵌套得非常深时。 Boost(一个明显的例子)有一些名称,如果每次都使用完全限定名称,那将会太长而不便于使用。这对于(现在,幸运的是,大部分已经过时的)Boost Lambda库尤其如此,其中您使用像_1这样的占位符,如果您坚持使用完全限定名称,那将变成类似于boost::lambda::placeholders::_1的东西(但我从记忆中得出,所以可能部分错误),这将击败使用lambda库的主要目的。

我认为这里添加了一些有价值的信息。在这些情况下,选择实际上很重要,需要考虑一些事情。感谢您提出这个问题。 - qrikko
2
我认为,在这种情况下,您最好使用using std::swap; - Andrei Tita

2

完全没有性能提升或惩罚。所有的调用和变量在编译时都被解析。

三者之间的选择有点主观。是的,using namespace <ns>; 有时会因为污染全局命名空间而受到反对,但是我认为对于小文件来说使用它是安全的。

我倾向于在测试目的中使用第二个选项,因为我预期会出现冲突,但之后我会将其删除。这可能会变得更加混乱,因为你最终会得到既限定又未限定名称的组合:

vector<std::string> x;

因为你在顶部使用了using std::vector;,但没有使用using std::string;
我更喜欢第三个。

1

你的代码性能不会受到任何影响,这只是一个编译时的问题。理论上,它可能会对编译时间产生一些影响,但我怀疑它将永远达不到可测量的比例。

using namespace std(或其他任何命名空间)绝对应避免在头文件中使用,因为头文件可以被包含在任何地方,引入其中的符号可能导致歧义。

通常,命名空间存在于避免名称冲突,并且using namespace破坏了这个目的。using the_namespace::some_id也有这种倾向,但程度较轻。对于你的问题,没有明确的答案,但我通常遵循以下规则:

  1. 永远不要在头文件中使用using namespace
  2. 避免使用using namespaceusing,除非可以节省大量的打字时间。必要时使用命名空间别名(即namespace abbrv = some_really::long_and::nested_namespace;)。
  3. 尽量限制using的范围:可以将其放入函数和块以及命名空间范围内。也就是说,如果您在.cpp文件中有一个记录日志的函数,请将using std::cout;using std::endl;(或其他您正在使用的内容)放入函数体中,而不是文件范围内。

0
主要原因是它可能会导致歧义(对于编译器和人类读者都是如此),并且它也可能会减慢编译本身的速度(但这不是那么大的问题,因为第一件事情是最重要的)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接