我在下面的链接中读到未命名(匿名)类不应该包含静态数据。有谁可以让我知道它的原因?
https://www-01.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.cbclx01/cplr038.htm 以下说..
程序中只能有一个静态成员的定义。 未命名的类,未命名的类中包含的类和本地的类 类不能有静态数据成员。
答案 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
与另一个头文件中的名称冲突。