C++ C++;通过空指针的静态常量访问

C++ C++;通过空指针的静态常量访问,c++,pointers,static,C++,Pointers,Static,这是定义的行为吗?我读过C++标准,但是找不到任何关于访问静态const值的方法…我已经检查了GCC 4.2、Clang++和Visual Studio 2010生成的程序集,它们都没有执行对空指针的解引用,但我想确定一下。我认为调用时根本没有使用类型的实际值 class Foo { public: static const int kType = 42; }; void Func() { Foo *bar = NULL; int x = bar->kType; putc(x,

这是定义的行为吗?我读过C++标准,但是找不到任何关于访问静态const值的方法…我已经检查了GCC 4.2、Clang++和Visual Studio 2010生成的程序集,它们都没有执行对空指针的解引用,但我想确定一下。

我认为调用时根本没有使用类型的实际值

class Foo {
public:
 static const int kType = 42;
};

void Func() {
 Foo *bar = NULL;
 int x = bar->kType;
 putc(x, stderr);
}
由于
kType
是静态的,而bar是
Foo
类型,因此它与调用

bar->kType
为了清楚起见,你真的应该这么做

因此,在大多数平台上调用
bar->kType
会发出编译器警告。

您可以使用指针(或其他表达式)访问静态成员;然而,不幸的是,通过空指针这样做是官方未定义的行为。从9.4/2“静态成员”:

类X的静态成员s可以是 指使用限定id 表达式X::s;没有必要 使用类成员访问语法的步骤 (5.2.5)指静态构件。A. 静态构件可以使用 中的类成员访问语法, 对象表达式是哪种情况 已评估。

基于以下示例:

Foo::kType

其目的是让您确保在此场景中调用函数。

除了通过空指针进行访问的问题外,代码中还有另一个微妙的问题

$9.4.2/2-“静态数据成员在其类定义中的声明不是定义,可能是除cv限定void之外的不完整类型。静态数据成员的定义应出现在包含该成员类定义的命名空间范围中。”

$9.4.2/4-“如果静态数据成员是常量整数或常量枚举类型,则其在类定义中的声明可以指定常量初始值设定项,该初始值设定项应为整数常量表达式(5.19)。在这种情况下,该成员可以出现在整型常量表达式中。如果在程序中使用该成员,则该成员仍应在命名空间范围中定义,并且命名空间范围定义不应包含初始值设定项。”


因此,在OP代码中使用UB还有一个原因。

即使它有效,这也是一个糟糕的代码。

在严肃的编程中,您不仅为自己编写代码,也为维护代码的其他人编写代码。 必须避免玩这样的把戏,因为你尊重你的同事

此代码的一个结果是:指针是否为
NULL
甚至没有问题,但它意味着此成员
kType
可能不是类的普通非静态成员。有时类很大(这也很糟糕),人们不能总是重新检查每个变量的定义

要严格。并仅通过以下方式调用所有静态成员:

class Foo { 
public: 
 static const int kType = 42; 
}; 

int const Foo::kType;

void Func() { 
 Foo *bar = NULL; 
 int x = bar->kType; 
 putc(x, stderr); 
}
另一种可能是遵循一种编码约定,让大家知道该成员是静态的,例如,所有类静态成员的
s\uu
前缀:

Foo::kType

可以说,有一条更高的规则,基本上就是说——不要去想编译那些可以证明没有使用过的东西。高级模板编程很大程度上依赖于此,因此,即使编译器清楚地看到没有使用构造的结果时,它可能有点灰色地带,它也会消除它。特别是当它被证明是安全的,就像在这种情况下


如果您愿意,您可能想尝试一些变体,比如将指针设为函数的参数、函数的结果、使指针未初始化(触发编译器投诉的最佳机会)、直接转换为0(无投诉的最佳机会).

大多数编译器都应该对通过实例指针访问静态/常量成员发出警告。+1“为了清晰起见,您确实应该这样做”正确,静态成员独立于对象实例存储,因此对象本身永远不会被取消引用。@casablanca:这是一个实现细节。如果您有文档证明这是特定编译器版本的行为,那就太好了!否则,不要这样做。当然,就一个关于C++抽象机器的问题而言,这恐怕不是一个“正确”的答案。具体地说,我极力反对使用“Foo::kType”。我只是想向OP解释为什么它在实现上没有失败,这是现有答案当时没有解释的。我要分头指出“评估对象表达式”这并不一定意味着取消对指针的引用——这可能只是意味着找到指针值——但看看§5.2.5,似乎是这样,因为在这种情况下,“对象表达式”被定义为“
*bar
”,而不仅仅是“
bar
”。你说得对。这不是一个争论的话题吗?评价一个对象的概念,就像简单地命名它一样,是相当禅宗的。另一方面,该文本下的示例清楚地显示了函数调用。显然,他们的意思是“…”或“->”之前的表达被评价。Potatoswatter:我相信我的解释是正确的,但我当然不认为自己是权威。虽然我认为通过适当类型的空指针调用静态成员函数的可能性可能会如您所期望的那样工作,但是使用类型名而不是指针进行调用是很容易的(尽管我认为对于可能并不总是正确的模板),因此,您最好不要使用指针。另外,请注意,可能存在样式问题,因此您不愿意通过对象表达式调用静态成员函数:@Michael:是的。我试图找出标准中说去引用NULL是不好的地方。5.3.1中
*
的规范仅说明
Foo::kType
Foo::s_kType