【问题标题】:C++ Name ResolutionC++ 名称解析
【发布时间】:2013-01-03 10:09:56
【问题描述】:

我对 C++ 中的 namespaceusing 有点疑惑,基本上我想知道它们的区别并弄清楚如何以最佳方式使用它。

在我看来,(至少)有三种方法可以解析类名,但我不知道如何选择:

  1. using namespace <namespace>
  2. using <namespace>::<what_to_use>
  3. <namespace>::<what_to_use> <use_it>

我想知道这些优势,尤其是在以一种或另一种方式涉及性能的情况下,如果它只是语法和偏好问题,或者是否还有其他我没有考虑过的事情。

【问题讨论】:

  • 与运行时性能无关。
  • 我在大学时被告知,除了所有的命名空间都在范围内之外,使用using namespace std; 也是一个安全隐患。
  • 1.由于您描述的原因而气馁 2. 帮助您在 ADL 的帮助下使用其他人在他们自己的名称空间中提供的功能(请参阅std::swap,尽管让答案更好地向您解释这一点) 3. 我最喜欢但那是一个品味问题,如果你不像我那样喜欢到处看到std::,可能会很麻烦。但是它们都没有任何性能差异,它们只是关于名称解析并且不会“编译到实际代码中”

标签: c++ namespaces using


【解决方案1】:

首先是一个using namespace directive,它将来自指定命名空间的所有符号名称带到您当前的命名空间中,无论您是否需要/使用它们。当然不可取。

其次是 using namespace declaration。它只会在您当前的命名空间中带来指定的符号名称。优点是您不必每次都输入完全限定的名称。

第三个是符号的完全限定名称。缺点是您必须在使用符号的任何地方输入完全限定名称。

显然,第二和第三是更合适的。它们中的任何一个都没有性能差异。唯一的区别是您输入的字符数量。只需根据您的编码标准指定的内容进行选择。

编辑:
正如@Jerry 指出的那样,将声明与 ADL(参数依赖查找)结合使用会导致不良影响。
您可以在我的一个答案中找到详细的解释:

Detailed explanation on how Koenig lookup works with namespaces and why its a good thing?

在该部分下,
为什么要批评 Koenig 算法?

【讨论】:

  • 我有点喜欢 using namespace declaration 因为我可以把它放在我的 .cpp 文件中,而且我有一个列表,我看到“这是从这些命名空间中使用的这个文件”,但我也有点喜欢完全限定名称,因为他们一看到他们就告诉我这个功能来自这个命名空间。但好吧,我想我正在以某种形式这样做,并且从我得到的答案中,我觉得可以继续下去。感谢您花时间解释这一点。
  • @qrikko:通常会遵循组织的编码标准。否则,这是个人选择。尽管重要的是在做出选择后保持一致性。
  • @qrikko:不幸的是,如果您阅读我的回答,您会意识到 1)您并不像您想象的那么安全,以及 2)所有声称不可能的答案对性能的任何影响(间接)至少可能是错误的。他们是正确的,您指定名称的形式不会直接影响性能,但说(甚至暗示)性能不会受到影响仍然是错误的。
【解决方案2】:

在一种情况下(诚然,有些不常见),您使用的表单确实可以产生影响,而您想要使用的表单 using namespace foo,它最常应用于std 命名空间(即,您在其中编写 using namespace std;

最明显的例子是您正在为用户定义的类型编写排序。 可能这将应用于用户还定义了自己的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 库的大部分目的。

【讨论】:

  • 我相信这里添加了一些有价值的信息。需要考虑的事情,因为在这些情况下,选择哪个实际上确实很重要。感谢您提出。
  • 不过,我想说,在这种情况下,您更愿意使用using std::swap;
【解决方案3】:

没有任何性能增益或损失。所有调用和变量都在编译时解析。

三者之间的选择有些主观。是的,using namespace &lt;ns&gt;; 有时会因为污染全局命名空间而受到反对,我认为它可以安全地用于小文件。

我倾向于将第二个用于测试目的,我预计会发生冲突,但我会在之后将其删除。这可能会变得更加混乱,因为您最终可能会得到限定名称和非限定名称的组合:

vector<std::string> x;

因为您在顶部有一个using std::vector;,但没有using std::string;

我更喜欢第三个。

【讨论】:

    【解决方案4】:

    对代码性能的影响为零,这纯粹是编译时的事情。它可能(理论上)对编译时间有一些影响,但我怀疑这会达到可衡量的比例。

    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;(或您正在使用的任何内容)放入函数主体,而不是文件范围。

    【讨论】:

      【解决方案5】:

      主要原因是它可能导致歧义(对于编译器和人类阅读器),它还可能减慢编译本身的速度(但这并不是第一件事那么大的问题)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-14
        • 1970-01-01
        • 1970-01-01
        • 2011-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多