【问题标题】:Anonymous namespaces: Are they really that great?匿名命名空间:它们真的那么棒吗?
【发布时间】:2011-05-29 16:17:29
【问题描述】:

我长期以来一直使用static 关键字来定义内部链接。后来,我改用 C++ 风格,将本地事物包装在匿名命名空间中。

然而,当我使用匿名命名空间多年后,我开始认为static 关键字更容易使用!

一个常见的问题是我有这个模式:

namespace {
    // ...five pages of code...
}  // namespace

要查看某个函数是否具有内部或外部链接,我现在必须滚动很多次,而不是旧的 C 样式,我可以只检查函数/对象前面是否有 static

我知道匿名命名空间有一些 static 无法做到的事情 - 隐藏 typedef - 但我个人对此并不是很感兴趣。

您对此有何看法?匿名命名空间的胜利是否会降低可读性?还是我都错了?

【问题讨论】:

  • static 命名空间范围内的函数已被弃用,这是不使用它们的充分理由。 anon 命名空间中的函数具有static 的所有优点,除了它们具有外部链接。我认为后者已经由编译器处理,从导出的部分中删除匿名命名空间中定义的名称。
  • @Gene 实际上,静态函数从未被弃用。弃用仅针对objects。但是,在 n3225 中删除了弃用:在 C++0x 中,不再建议使用它来提供对象或函数内部链接。另一个区别是未命名的命名空间将保留外部链接:未命名的命名空间及其内容将在 C++0x 中获得内部链接。所以静态方式和命名空间方式之间似乎没有任何区别,除了命名空间方式将允许像namespace { int a; } int a; 这样的东西。
  • @Johannes:C++0x 是否允许带有内部链接的模板参数,或者这是一个巨大的突破性变化?
  • @Steve 是的,它允许模板参数具有内部链接。

标签: c++ static namespaces


【解决方案1】:

如果你的命名空间中的代码太长,没有什么可以阻止你这样做:

namespace {
    int foo(char* x) {
        return x[0] + x[1];
    }
}

namespace {
    int bar(char *x, char *y) {
        return foo(x) + foo(y);
    }
}

在 C++03 中,使用未命名命名空间的实际优势正是内容具有 external 链接(但在 TU 之外仍然不可见,因为无法引用它们)。模板参数不能有内部链接:

namespace {
    int foo(const char* x) {
        return x[0] + x[1];
    }
}

static int foo2(const char *x) {
    return x[0] + x[1];
}

template <int (*F)(const char*)>
void baz(const char *p) {
    F(p);
}

int main() {
    baz<foo>("ab");   // OK
    baz<foo2>("ab");  // not valid
}

【讨论】:

  • 是的,但这真的能让事情变得更好吗?更多的打字,更多的括号来平衡......
  • @kotlinksi:嗯,是你说这是一个可读性问题,你在几个代码屏幕中只输入了一次namespace {}。现在您说多次键入它是一个可写性问题。如果您可以通过内部链接管理使用功能,那么您可以使用static。如果出于某种原因您需要具有外部链接的函数,那么static 的优势是什么并不重要,因为您无法拥有它们。
  • @kotlinksy:我想另一种可能性是为这些函数创建一个头文件。给它一个名称,表明它的所有内容都在未命名的命名空间中,然后你可以通过它们所在的文件来判断函数在哪里。
【解决方案2】:

除了史蒂夫指出的非常有效的点之外,我还看到了匿名命名空间中其他非常重要的方面,这些方面使它们优于静态函数: 局部性、易于重构和信息隐藏

假设您有一个或两个类函数需要几个其他非常具体但不使用类成员的辅助函数。如果您坚持使用 Robert C. Martin(函数应该很小并且服务于一个明确定义的目的),您经常会发现大型函数可以重构为较小的函数,尽管这些较小的函数可能在一开始只用于前一个大函数。

那么你有什么选择:

  1. 立即创建一个新的(可能是私有的)类:

    这需要相当多的打字可能是矫枉过正的 - 让我们面对现实 - 每个人 有时懒惰或匆忙。

  2. 生成私有静态函数或非成员函数:

    如果你这样做,两者都需要编辑头文件和 cpp 文件 正确的,所以还有一点负担可能会打断你 工作流程超过必要并生成不必要的代码 使您的头文件混乱,可能需要前向声明或 甚至附加包含在您的标题中。

  3. 匿名命名空间:

    你有一个不需要的辅助函数 成员访问和服务一个目的-> 把它放在那里写这个 函数接近将使用它的类方法。这是大体上 我的首选:它很快,并且不会使头文件混乱。 命名空间清楚地表明:除了这个 cpp 之外,没有其他任何东西使用它。不 朋友会使用它,没有图书馆用户会知道它的存在。你 再明显不过了,而且这种范式通常会导致 更简洁的功能设计,输入参数少,只有一个 输出修改。此外,您还有功能位置:在主要之前定义 采用。虽然这可能是一个缺点,但我发现它在浏览 大类的实现。另一个优点是常量跨越多个 功能,但对于图书馆用户来说并不是很有趣。把它们放在 命名空间也是如此,最好与使用它们的函数相同。如果转 稍后你需要其他地方的常量和函数,转换 整成一个班级,已经收拾得整整齐齐了。

免责声明:许多人可能会争辩说,使用 pimpl 更清洁。这只是我个人的看法。

【讨论】:

    【解决方案3】:

    匿名命名空间是唯一不会让类声明污染全局范围的东西。在 .cpp 文件中定义类时非常有用。

    【讨论】:

      猜你喜欢
      • 2011-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多