通过无效指针访问静态成员:保证"工作"?

Lig*_*ica 9 c++

建立

给定此用户定义的类型:

struct T
{
    static int x;
    int y;

    T() : y(38);
};
Run Code Online (Sandbox Code Playgroud)

并在某处有用的必要定义:

int T::x = 42;
Run Code Online (Sandbox Code Playgroud)

以下是将标准int值传递给stdout的规范方法:

std::cout << T::x;
Run Code Online (Sandbox Code Playgroud)

控制

同时,由于T不存在的实例,以下(当然)无效:

T* ptr = NULL; // same if left uninitialised
std::cout << ptr->y;
Run Code Online (Sandbox Code Playgroud)

现在考虑以下代码的可怕和邪恶和坏:

T* ptr = NULL;
std::cout << ptr->x; // remember, x is static
Run Code Online (Sandbox Code Playgroud)

ptr如上所述,取消引用无效.即使这里没有物理内存解除引用,我相信它仍然算作一个,使上面的代码UB.或者......是吗?

14882:2003 5.2.5/3明确表示a->b转换为(*(a)).b,并且:

评估点或箭头之前的后缀表达式; 即使结果不必确定整个后缀表达式的值,也会发生此评估,例如,如果id-expression表示静态成员.

但目前尚不清楚这里的"评估"是否涉及实际的解除引用.实际上,14882:2003和n3035似乎都没有明确地说明指针表达式在处理静态成员时是否必须求值为指向有效实例的指针.

我的问题是,这有多么无效?它是否真的被标准特别禁止(即使没有物理解除引用),或者它只是我们可以逃脱的语言的怪癖?即使它被禁止,我们还期望GCC/MSVC/Clang在多大程度上安全地对待它?

我的g ++ 4.4似乎生成的代码永远不会尝试将[invalid] this指针推入堆栈,并关闭优化.

BTW如果T是多态的,那么这不会影响这一点,因为静态成员不能是虚拟的.

Jam*_*lis 11

目前尚不清楚这里的"评估"是否涉及实际的解除引用.

我在这里读"评估"为"评估子表达式".这意味着将对一元*进行求值,并通过空指针执行间接,从而产生未定义的行为.

此问题(通过空指针访问静态成员)在另一个问题中讨论,何时在null实例上调用成员函数会导致未定义的行为? 虽然它具体讨论了成员函数,但我认为数据成员在这方面没有任何不同的理由.那里有一些很好的讨论问题.

针对C++标准报告了一个缺陷,询问"通过空指针调用静态成员函数是否未定义?" (参见CWG缺陷315)此缺陷已关闭,其解决方案表明通过空指针调用静态成员函数是有效的:

p->f()(*p).f()根据5.2.5 [expr.ref] 重写.除非将左值转换为右值,否则为null *p时不是错误p

但是,这个决议实际上是错误的.

它预先假定了"空左值"的概念,它是另一个缺陷CWG缺陷232的建议解决方案的一部分,CWG缺陷232询问更一般的问题,"间接通过空指针未定义的行为吗?"

对该缺陷的解决方案将使通过空指针(如调用静态成员函数)的某些形式的间接有效.但是,该缺陷仍然存在,其解决方案尚未被C++标准采用.在该缺陷被关闭并且其解决方案被合并到C++标准之前,通过空指针进行间接寻址(或者解除引用空指针,如果更喜欢该术语)总是会产生未定义的行为.