今天我发现我曾经长期存在的东西(我的意思是长 - 像二十年一样),在C ++中被认为是非法的,实际上是合法的。即,调用静态成员函数,就好像它属于单个对象一样。例如:
struct Foo
{
static void bar() { cout << "Whatever."; }
};
void caller()
{
Foo foo;
foo.bar(); // Legal -- what?
}
我通常会看到使用“范围分辨率语法”严格调用静态成员函数,因此:
Foo::bar();
这是有道理的,因为静态成员函数不与类的任何特定实例相关联,因此我们不希望特定实例在语法上“附加”到函数调用。
然而我今天发现GCC 4.2,GCC 4.7.1和Clang 3.1(作为编译器的随机抽样)接受前一种语法,以及:
Foo* foo = new Foo;
foo->bar();
在我的特殊情况下,这个表达式的合法性导致了运行时错误,这使我确信这种语法的特殊性不仅仅是学术兴趣 - 它具有实际后果。
为什么C ++允许调用静态成员函数,就好像它们是单个对象的直接成员一样 - 即使用。或 - &gt;附加到对象实例的语法?
答案 0 :(得分:14)
在 C ++的设计和演变(第288页)中,Bjarne Stroustrup提到在静态成员函数之前的几天,程序员使用像((X*)0)->f()
这样的黑客来调用没有成员函数的成员函数需要一个对象。我的猜测是,当静态成员函数添加到该语言时,允许通过->
进行访问,以便具有此类代码的程序员可以将f
更改为static
,而无需进行搜索和更改每次使用它。
答案 1 :(得分:9)
据推测,你可以在你可能不知道类的东西的地方调用它,但编译器会这样做。
假设我有一堆类,每个类都有一个返回类名的静态成员:
class Foo
{
static const char* ClassName() { return "Foo"; }
};
class Bar
{
static const char* ClassName() { return "Bar"; }
};
然后在我的代码中我可以执行以下操作:
Foo foo;
printf( "This is a %s\n", foo.ClassName() );
无需担心一直知道我的对象类。例如,在编写模板时,这将非常方便。
答案 2 :(得分:6)
就像这样,因为标准说它是如何运作的。 n3290§9.4声明:
可以使用qualified-id引用类X的静态成员 表达式
X::s;
没有必要使用类成员访问 语法(5.2.5)引用静态成员。静态成员可以是 引用使用类成员访问语法,在这种情况下 对象表达式进行评估。 [例如:struct process { static void reschedule(); }; process& g(); void f() { process::reschedule(); // OK: no object necessary g().reschedule(); // g() is called }
结束例子]
答案 3 :(得分:6)
来自The Evolution of C++ (pdf), section 8. Static Member Functions:
......还观察到了不可移植的代码,例如
((x*)0)->f();
用于模拟静态成员函数。
所以我的猜测是(基于几乎所有其他奇怪的语法事物的基本原理模式),当你只是提供一个类型来提供向后兼容已建立但破坏的习语时,它们允许调用静态成员函数。
答案 4 :(得分:1)
如果你没有订阅“因为标准这么说”的因果关系学派,我还建议静态方法已经足够老了,以至于人们实际上担心通过{{1函数调用的参数,因此将纯函数“静态”作为优化可能在1985年风靡一时。