以静态成员C++ FAQ的地址为例

P0W*_*P0W 7 c++

是什么这个 C++ FAQ试图传达?

如果(并且仅当)它具有类外定义,您可以获取静态成员的地址:

class AE {
    // ...
public:
    static const int c6 = 7;
    static const int c7 = 31;
};

const int AE::c7;   // definition

int f()
{
    const int* p1 = &AE::c6;    // error: c6 not an lvalue
    const int* p2 = &AE::c7;    // ok
    // ...
}
Run Code Online (Sandbox Code Playgroud)

然而这编译!

Csq*_*Csq 9

-O2用来编译.编译器可以优化掉const int* p1 = &AE::c6;赋值(因为它没有效果),因此它不需要AE::c6最终代码中的地址,这就是它编译的原因.

它在没有优化的情况下提供链接器错

如果您开始使用p1(例如std::cout << p1 << p2 << std::endl;)链接,也会收到链接器错误


Jam*_*nze 5

常见问题解答中的评论非常误导; 这两个AE::c6AE::c7是左值.如果没有定义AE::c7,则相关代码违反了一个定义规则:

除非它是未评估的操作数或其子表达式,否则可能会评估表达式.名称显示为潜在评估表达式的变量是odr-used,除非它是满足出现在常量表达式中的要求的对象,并且立即应用左值到右值转换.[...]

[...]

每个程序应该只包含该程序中使用的每个非内联函数或变量的一个定义; 无需诊断.

实际上,如果 编译器实际需要对象的地址,链接器通常会生成错误.在您的情况下,如果p2以后不使用,那么编译器将不需要该地址,因为优化将删除定义p1.发生这种情况的更常见的情况如下:

std::vector<int> v;
v.push_back( AE::c6 );
Run Code Online (Sandbox Code Playgroud)

由于std::vector<>::push_back需要引用,因此没有立即的左值到右值转换,并且需要定义.在实践中,std::vector<>::push_back是一个模板函数(通常是内联的),因此编译器可以看到它的实现,并将值传播到函数中,实际发生左值到右值转换的位置,代码将编译和工作.但它仍然是正式未定义的行为.