在匿名命名空间中的自由函数

4
//file.h

namespace Foo{
namespace{
   void func(){}
}
}

vs

namespace Foo{ 
   void func(){} 
}

//file2.cpp use file.h's method

这两种方法在调用代码中(例如可见性方面)会有什么后果(如果有的话)?

1
在头文件中不应使用未命名的命名空间:https://dev59.com/QGsz5IYBdhLWcg3wWmfL - moooeeeep
1
嗯,我不确定你所说的“调用代码的后果”是什么意思。据我所知,调用特定代码的后果始终相同,无论它位于哪个命名空间中。命名空间仅限制其中声明内容的可见性和局部性。 - luk32
3个回答

6

This:

namespace Foo {
namespace {

void func() {}

}
}

大体上等同于这个:

namespace Foo {

static void func() {}

}

static 的情况下,函数具有内部链接,因此连接器无法访问它。在未命名命名空间的情况下,函数具有外部链接(对连接器可见),但是使用一个其他源文件“通常”无法访问的名称。例如,您可以通过反向工程编译器的名称重整方案从不同的源文件调用该函数,并且该函数仍然列在对象文件的符号中。
但是,共同点是包含代码的每个源文件(可能通过#include头文件)都将包含其自己的函数副本。这可能会影响二进制文件的大小。
另外,如果出于某种原因您需要第一个选项,应当详细记录说明。在头文件中使用未命名命名空间通常是一个“奇怪”的点,您不想在代码中遇到这种情况。我必须说我想不到这种情况有什么实际用途。

1
第一个等同于:
namespace Foo
{
    namespace TranslationUnitSpecific
    {
        void func();
    }
}

这意味着每次包含头文件时,您都会声明一个新的、不相关的 func 实例。如果 func 不是 inline,则必须在每个使用它的源文件中定义它。(另一方面,这意味着您可以在头文件中提供实现,而不必使函数成为 inline。)
这也意味着您不能在头文件中使用函数并在 inline 或模板函数中使用,否则有违反单一定义规则的风险,可能导致未定义的行为。
一般来说,在头文件中很少有任何情况需要使用未命名命名空间。

1
两种变体都允许在名称为 Foo::func() 的情况下找到该函数。但是编译器可能会在这两种情况下生成不同的代码。在匿名命名空间内声明一个函数使该函数局限于 .cpp 文件。也就是说,每个包含头文件的 .cpp 文件可能最终都会有自己的(相同的)func 代码实例。这可能会导致最终可执行文件中出现一些膨胀,因为代码被重复了。请注意,如果您定义了内联函数(如您的问题所建议的那样),则这不是真正的问题,因为由于内联而无论如何都会复制代码。尽管如评论中所指出的那样:头文件中的匿名命名空间很少见,并且会引起任何审核此代码的人的怀疑。除非您有非常好的理由,否则应始终优先选择第二个选项。

因此,副作用将是相同的,只是实际生成的二进制代码将被复制,就像函数被声明为“inline”或“static”一样。对于后者而言,未命名的命名空间实际上是C++中更优秀的方法。 - luk32

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