【发布时间】:2011-07-12 01:21:21
【问题描述】:
设置
给定这个用户定义的类型:
struct T
{
static int x;
int y;
T() : y(38);
};
并将必要的定义放在有用的地方:
int T::x = 42;
以下是将int 的值流式传输到stdout 的规范方式:
std::cout << T::x;
控制
同时,由于T 的实例不存在,以下内容(当然)无效:
T* ptr = NULL; // same if left uninitialised
std::cout << ptr->y;
问题
现在考虑下面的可怕、邪恶和糟糕的代码:
T* ptr = NULL;
std::cout << ptr->x; // remember, x is static
如上所述,取消引用 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 指针压入堆栈,优化已关闭。
顺便说一句如果T 是多态的,那么这不会影响这一点,因为静态成员不能是虚拟的。
【问题讨论】:
-
When does invoking a member function on a null instance result in undefined behavior? 对此进行了一些相关的讨论(您的问题是关于静态数据成员;静态成员函数在另一个问题中进行了讨论)。共识是它应该是允许的,但不是;有一个封闭的 DR 可以允许,但 DR 依赖于对仍然开放的 DR 的决议(该共识是否正确尚待辩论:-D)
-
问题是已关闭的 DR(CWG 缺陷 315)说“当
p为空时,*p不是错误,除非将左值转换为右值。”但是,这依赖于“空左值”的概念,这是对仍然开放的CWG Defect 232 提出的解决方案的一部分。 -
@James:所以我们说这只是 C++ 美味混乱的另一个不确定的怪癖? :)
-
实际上,是的。我几乎可以肯定,在你的引用中,“评估”的意思是“操作员做的事情”,而
*所做的事情是“执行间接”,所以你最终通过空指针执行间接,这会产生 undefined行为。 -
@James:哦,太高兴了。顺便说一句,我会接受这个答案。
标签: c++