我在下面的链接中读到,未命名(匿名)类中不应该有静态数据成员。请问有什么原因?
https://www-01.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.cbclx01/cplr038.htm 中提到如下内容:
只能在程序中定义静态成员一次。未命名类、包含在未命名类中的类和局部类不能有静态数据成员。
只能在程序中定义静态成员一次。未命名类、包含在未命名类中的类和局部类不能有静态数据成员。
如果static
成员数据被进行了ODR使用,则必须在类/结构体之外定义。
struct Foo
{
static int d;
};
int Foo::d = 0;
如果类/结构体没有命名,则无法在类外定义成员。int ::d = 0;
不能用于定义未命名类的静态成员。
如果您能使用C++17或更高版本,则可以使用
static inline int d = 10;
这将允许在匿名的类
/结构体
中定义一个静态
成员变量。
以下示例代码演示了一个静态
成员变量不需要在类定义外部定义:
#include <iostream>
struct foo
{
static inline int d = 10;
};
int main()
{
auto ptr = &foo::d;
std::cout << *ptr << std::endl;
}
构建命令:
g++ -std=c++17 -Wall socc.cc -o socc
程序运行的输出结果:
10
感谢 @Jean-Michaël Celerier 提供更新建议。
你确定标准实际上禁止这样做吗?
正如所提到的,问题在于需要对静态成员进行实际定义。语言没有提供定义它的方法。在结构体内部或通过其实例引用它没有其他问题。
然而,例如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
并不是真正的匿名,但至少它将被限制在翻译单元中。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
}
decltype(a)::j
东西赋予什么名称以进行链接。因此,大多数(全部?)编译器 - 包括正常模式下当前版本的GCC - 拒绝编译此代码。a
的声明移动到包含在不同源文件中的头文件中,则它们仍然在链接阶段失败。namespace {
struct whatever {
static int j;
} a;
int whatever::j = 42;
}
int main() {
return a.j == 42 ? 0 : 1;
}
whatever
与另一个头文件中的名称冲突也不会导致问题。
struct { int x; } a_variable; &decltype(a_variable)::x
可以工作,所以没有理由decltype(a_variable)::static_data_member;
不能工作。 - Jean-Michaël Celerier