简体   繁体   English

匿名命名空间类定义

[英]Anonymous Namespace Class Definition

I was looking over some (C++) code and found something like this:我正在查看一些 (C++) 代码并发现类似这样的内容:

//Foo.cpp
namespace
{
    void SomeHelperFunctionA() {}
    void SomeHelperFunctionB() {}
    void SomeHelperFunctionC() {}

    //etc...    

    class SomeClass //<---
    {
        //Impl
    };
}

SomeHelperFunction[AZ] are functions that are only needed in that translation unit, so I understand why they're in an anonymous namespace . SomeHelperFunction[AZ]是仅在该翻译单元中需要的函数,所以我理解为什么它们位于匿名namespace中。 Similarly, SomeClass is also only required in that translation unit, but I was under the impression that you could have classes with identical names in different translation units without any sort of naming collisions provided that you didn't have a global class declaration (eg, in a commonly included header file).同样, SomeClass也只在该翻译单元中是必需的,但我的印象是,如果您没有全局类声明(例如,在通常包含的头文件中)。

I should also mention that this particular translation unit does not include any headers that might declare a class with an identical name ( SomeClass ).我还应该提到,这个特定的翻译单元包含任何可能声明具有相同名称 ( SomeClass ) 的类的标头。

So, given this information, could someone please shed some light on why the original programmer might have done this?那么,鉴于此信息,有人可以阐明为什么原始程序员可能会这样做吗? Perhaps just as a precaution for the future?也许只是为了将来的预防措施?

I'll be honest, I've never seen classes used in anonymous namespaces before.老实说,我以前从未见过在匿名名称空间中使用的类。

Thanks!谢谢!

An anonymous namespace is like the static keyword when it is applied at the global level.匿名命名空间在全局级别应用时类似于 static 关键字。

An anonymous namespace makes it so you can't call anything inside the namespace from another file.匿名命名空间使您无法从另一个文件调用命名空间内的任何内容。

Anonymous namespaces allow you to limit the scope of what's within to the current file only.匿名命名空间允许您将其中内容的范围限制为仅当前文件。

The programmer would have done this to avoid naming conflicts.程序员会这样做以避免命名冲突。 No global names will conflict in this way at linking time .在链接时,全局名称不会以这种方式发生冲突。

Example:例子:

File: test.cpp文件:test.cpp

namespace 
{
  void A()
  {
  }
  void B()
  {
  }
  void C()
  {
  }
}

void CallABC()
{ 
  A();
  B();
  C();
}

File: main.cpp文件:main.cpp

void CallABC();//You can use ABC from this file but not A, B and C

void A()
{
//Do something different
}

int main(int argc, char** argv)
{
  CallABC();
  A();//<--- calls the local file's A() not the other file. 
  return 0;
}

The above will compile fine.以上将编译正常。 But if you tried to write an CallABC() function in your main you would have a linking error.但是,如果您尝试在 main 中编写CallABC()函数,则会出现链接错误。

In this way you can't call A() , B() and C() functions individually, but you can call CallABC() that will call all of them one after the other.通过这种方式,您不能单独调用A()B()C()函数,但您可以调用CallABC() ,这将一个接一个地调用所有这些函数。

You can forward declare CallABC() inside your main.cpp and call it.您可以在 main.cpp 中转发声明CallABC()并调用它。 But you can't forward declare test.cpp's A(), B() nor C() inside your main.cpp as you will have a linking error.但是您不能在 main.cpp 中转发声明 test.cpp 的 A()、B() 或 C(),因为您将遇到链接错误。

As for why there is a class inside the namespace.至于为什么命名空间里面会有一个类。 It is to make sure no external files use this class.这是为了确保没有外部文件使用此类。 Something inside the.cpp probably uses that class. .cpp 中的某些内容可能使用该类。

If someone links this code and has the definition of an identical named class included and this file is linked before the other implementation you would have a name conflict.如果有人链接此代码并包含相同命名类的定义,并且此文件在其他实现之前链接,您将遇到名称冲突。

In the C++ ISO standard (Section 2.3), you will find a rule called The One Definition Rule .在 C++ ISO 标准(第 2.3 节)中,您会发现一条名为The One Definition Rule 的规则

Due to the complex relationship in C++ between compilation and linking, this is not a very simple rule.由于 C++ 中编译和链接之间的复杂关系,这不是一个非常简单的规则。 Most of the details are here .大部分细节都在这里

But it applies to the functions that are members of a class, because they are (at the linking level) simply functions with long names.但它适用于作为类成员的函数,因为它们(在链接级别)只是具有长名称的函数。

It applies in a slightly different way to templates, because the linker will happily discard extra definitions of template classes or functions that appear in separate translation units (source files).它以略微不同的方式应用于模板,因为链接器会愉快地丢弃出现在单独翻译单元(源文件)中的模板类或函数的额外定义。 This means that if you provide two different definitions of the same template, your program has undefined behaviour (best case scenario: one of the definitions will be silently chosen at random).这意味着,如果您提供同一模板的两个不同定义,您的程序将具有未定义的行为(最佳情况:将随机选择其中一个定义)。

I was under the impression that you could have classes with identical names in different translation units without any sort of naming collisions provided that you didn't have a global class declaration我的印象是,如果您没有全局类声明,您可以在不同的翻译单元中使用具有相同名称的类而不会出现任何命名冲突

Well, that's not the case.好吧,事实并非如此。 Remember that those "common" global class definitions are in header files.请记住,那些“通用”全局类定义在头文件中。 Those are literally included, copying that common global class definition to all translation units.那些字面意思包括在内,将通用的全局类定义复制到所有翻译单元。 If you use another way of including exactly the same class definitions in multiple translation units (eg. macro expansion), it's fine too.如果您使用另一种方式在多个翻译单元中包含完全相同的类定义(例如宏扩展),那也很好。 But if you have different definitions for the same class name, you risk undefined behavior.但是如果你对同一个类名有不同的定义,你就有未定义行为的风险。 Link failures if you're lucky.如果幸运的话,链接失败。

To answer your question directly, I think that this is to avoid linker "multiple definition" errors for static members of SomeClass.直接回答你的问题,我认为这是为了避免 SomeClass 的静态成员的链接器“多重定义”错误。 That is, suppose SomeClass is defined in a cpp file without an anonymous namespace and there are some static fields and/or static methods in it and these static members are defined next to the class definition in this very cpp-file.也就是说,假设 SomeClass 是在没有匿名命名空间的 cpp 文件中定义的,并且其中有一些静态字段和/或静态方法,并且这些静态成员是在这个 cpp 文件中的类定义旁边定义的。 Then these static members get the external linkage (are present in the symbol table of the corresponding.o file as GLOBAL).然后这些静态成员得到外部链接(在对应的.o文件的符号表中作为GLOBAL存在)。

Now you have another cpp-file and you want to create another SomeClass (unrelated to that one in the first cpp-file and you may be even unaware of the existence of the first SomeClass in that first cpp-file) for your own purposes and again without an anonymous namespace.现在你有了另一个 cpp 文件,你想创建另一个 SomeClass(与第一个 cpp 文件中的那个无关,你甚至可能不知道第一个 cpp 文件中第一个 SomeClass 的存在)用于你自己的目的和再次没有匿名名称空间。 And you define a static member with the same name as a static member of the first SomeClass.并且您定义了一个与第一个 SomeClass 的静态成员同名的静态成员。 And here you are: you end up with a linkage conflict.你在这里:你最终会遇到链接冲突。

So the author of the first cpp-file should have hidden the first SameClass inside an anounimous namespace, as the class was clearly supposed to be an implementation detail, rather than a class re-used by other people.因此,第一个 cpp 文件的作者应该将第一个 SameClass 隐藏在一个统一的命名空间中,因为该类显然应该是一个实现细节,而不是其他人重用的类。 And so its static members should not have external linkage.所以它的静态成员不应该有外部链接。

So overall, what I try to say is that non-constexpr/non-inline static members of a class are just like global variables or non-inline functions.所以总的来说,我想说的是类的非 constexpr/非内联静态成员就像全局变量或非内联函数。 And so their linkage can be made internal using an anonymous namespace just like it can be done for global variables and functions using static keyword or again an anonymous namespace.因此,可以使用匿名命名空间在内部建立它们的链接,就像使用 static 关键字或匿名命名空间对全局变量和函数进行链接一样。

// .cpp file
namespace 
{
    struct A
    {
        static int i;
    };
}

int A::i;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM