未命名的类C ++中的静态数据成员

时间:2016-01-07 07:01:59

标签: c++

我在下面的链接中读到未命名(匿名)类不应该包含静态数据。有谁可以让我知道它的原因?

https://www-01.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.cbclx01/cplr038.htm 以下说..

  

程序中只能有一个静态成员的定义。   未命名的类,未命名的类中包含的类和本地的类   类不能有静态数据成员。

3 个答案:

答案 0 :(得分:9)

所有static成员数据(如果它们是ODR使用的)必须在类/结构之外定义。

struct Foo
{
    static int d;
};

int Foo::d = 0;

如果类/结构未命名,则无法在类外定义成员。

int ::d = 0;

不能用于定义未命名类的静态成员。

答案 1 :(得分:1)

你确定标准实际上禁止这个吗?

如上所述,问题出现了,因为您需要具有静态成员的实际定义。该语言没有提供定义它的方法。引用它没有其他问题,因为我们可以在struct内或通过它的实例来实现它。

然而,例如GCC将接受以下内容:

static struct {
    static int j;
} a;

int main() {
    return a.j; // Here we actually refers to the static variable
}

但它无法链接,因为a.j指的是未定义的符号(._0::j),但有一种方法可以解决这个问题。通过在汇编程序中定义它或使用编译器扩展,您可以。例如,添加行

int j asm("_ZN3._01jE") = 42;

会让它发挥作用。在这种情况下,_ZN3._01jE是静态变量的真正损坏名称,无论是破坏的还是未修改的名称都不能直接用作标准C ++中的标识符(但它可以通过GCC扩展或汇编程序)。

您必须意识到这只适用于特定的编译器。其他编译器会以其他方式破坏名称(甚至做其他可能使得这些技巧根本不起作用的东西)。

您应该真正质疑为什么要使用此技巧。如果你可以使用标准方法完成工作,你最应该选择它。例如,您可以使用匿名namespace来降低可见性,例如:

namespace {
    static struct Fubar {
         static int j;
    } a;

    Fubar::a = 0;
}

现在Fubar并不是真正的匿名,但它至少会被限制在翻译单元。

答案 2 :(得分:1)

当 C++ 被标准化时,未命名的类不能有静态数据成员,因为无法定义/实例化它们。但是,这个问题已经在 C++11 中解决了,因为它添加了 decltype 操作符:

struct {
    static int j;
} a;

// Declare the static member of the unnamed struct:
int decltype(a)::j = 42;

int main() {
    return a.j == 42 ? 0 : 1; // Use the static member 
}

因此,原则上,可能存在带有静态数据成员的未命名类或结构。但是 C++ 标准制定者故意不允许这种语法,因为编译器不知道它应该为链接的 decltype(a)::j 事物赋予哪个名称。所以大多数(全部?)编译器——包括当前版本的正常模式下的 GCC——拒绝编译它。

-fpermissive 模式下,GCC-9 和 GCC-10 接受此代码并正常编译。但是,如果将 a 的声明移到一个头文件中,该文件包含在不同的源文件中,它们仍然会在链接阶段失败。

因此未命名的类只能在单个翻译单元中使用。为避免污染全局命名空间,只需将需要保留在本地的任何内容放在匿名命名空间中。所以:

namespace {
    struct whatever {
        static int j;
    } a;
    int whatever::j = 42;
}
int main() {
    return a.j == 42 ? 0 : 1; 
}

编译良好,不会污染全局命名空间,甚至不会导致问题,如果名称 whatever 与另一个头文件中的名称冲突。