C++ 为什么使用调用静态成员函数。或->;语法合法?

C++ 为什么使用调用静态成员函数。或->;语法合法?,c++,static,member-functions,C++,Static,Member Functions,可能重复: 今天我发现我有很长时间(我的意思是说,二十年),认为C++中的非法行为实际上是合法的。也就是说,调用静态成员函数就像它属于单个对象一样。例如: struct Foo { static void bar() { cout << "Whatever."; } }; void caller() { Foo foo; foo.bar(); // Legal -- what? } 这是有意义的,因为静态成员函数不与类的任何特定实例相关联,因此我们

可能重复:

今天我发现我有很长时间(我的意思是说,二十年),认为C++中的非法行为实际上是合法的。也就是说,调用静态成员函数就像它属于单个对象一样。例如:

struct Foo
{
    static void bar() { cout << "Whatever."; }
};

void caller()
{
    Foo foo;
    foo.bar();    // Legal -- what?
}
这是有意义的,因为静态成员函数不与类的任何特定实例相关联,因此我们不希望特定实例在语法上“附加”到函数调用

然而,我今天发现GCC4.2、GCC4.7.1和Clang 3.1(作为编译器的随机抽样)接受前一种语法,并且:

Foo* foo = new Foo;
foo->bar();

在我的特殊情况下,这个表达式的合法性导致了一个运行时错误,这使我确信这个语法的特殊性不仅仅是学术意义,它还有实际的后果


为什么C++允许静态成员函数被调用,就像它们是独立对象的直接成员,也就是说,使用或者->附加到对象实例的语法?

它是这样的,因为标准上说它就是这样工作的。n3290§9.4规定:

类X的静态成员s可以使用限定id引用 表达式
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 
} 
[结束示例]


假设这样,您可以在不知道某个对象的类类型但编译器知道的地方调用它

假设我有一组类,每个类都有一个返回类名的静态成员:

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() );    

不必一直担心知道我的对象的类。例如,在编写模板时,这将非常方便。

如果您不订阅“因为标准这么说”因果关系学派,我还建议,静态方法已经足够古老了,因为人们实际上担心将
参数传递给函数调用会带来额外的开销,因此,将纯函数“静态”作为一种优化在1985年很可能风行一时

> P>在第288页的C++设计和演化中,Bjarne Stroustrup提到在静态成员函数之前的日子里,程序员使用诸如“代码>(x(x))0)-f*()或代码>之类的黑客来调用不需要对象的成员函数。我的猜测是,当静态成员函数被添加到语言中时,允许通过
->
进行访问,这样具有类似代码的程序员就可以将
f
更改为
static
,而无需查找并更改它的每次使用。

来自:

…还观察到不可移植的代码,例如

用于模拟静态成员函数


因此,我的猜测是(基于几乎所有其他奇怪语法的基本原理模式),当您刚刚拥有与已建立但已损坏的习惯用法向后兼容的类型时,他们允许调用静态成员函数。

Hmmm,为什么不?区别在于作为隐式参数传递的实例是否与成员函数相同。无论哪种方式,编译器都可以访问函数指针本身。“在我的特殊情况下,此表达式的合法性导致了运行时错误”您可以对此进行扩展吗?我记得当我在.h中声明函数并在.cppHm中定义函数时,会出现编译器错误,与这里的第一个答案信息相同。但我不认为“因为圣经这么说”是一个恰当的答案。我不认为圣经是一个很好的参考(基于信仰)。但基于“标准”的答案是本网站的最终目标。该网站不是为了回答“为什么”这样的问题而设计的(因为这纯粹是猜测,因此信仰不是事实)。“我们希望这个网站上有事实而不是信仰,”拉斐尔巴普蒂斯塔说,“回答这个问题就是猜测。”。我怀疑“这是一个静态函数”被认为是一个实现细节,与使用它的人无关。但是“因为圣经是这么说的”,对于软件工程来说,这并不是一个充分的答案。圣经为什么这么说?为什么标准委员会认为这是一件可取的事情?从本质上看,这似乎令人困惑,因为对象被忽略(除了它的类型之外),规则改变了对象的含义。和->。@OldPeculier我认为“正确”解决这些问题的唯一方法是使用规范。C++是C++,因为它是。没有其他理由。当然,详细说明语言为何如此设计的参考资料会给出“为什么?”,但这些基本原理信息似乎经常丢失。。也许有一个引用被埋藏在某个文件或采访手稿中。“为什么”的答案可能是因为这是一个安全的扩展,它增加了一点灵活性,而没有破坏事情的危险。C++标准()没有理由,因此,标准的特定部分通常不会被解释。这个答案开始具有一些吸引力。但我不认为在实践中这会有任何好处。(也许你可以想出一个例子。)我认为模板,正如你提到的,将是最好的用例。因此,要调整第二个代码块:
template void caller(T&T){printf(“这是一个%s\n”,T.ClassName();}
可以工作。但是,
printf(“这是一个%s\n,T::ClassName())也可以工作“< /Cord>”和“后者何时会混淆,更不可理解,当前者不是”?OrdPululi:考虑一个改变上面代码的情况,以便<代码> Fo-Fo;< /C> >现在是“代码> Bo.Fo;。这是您使用<代码> Fo.CaseNAMEL()/代码>时唯一需要做的更改。如果您已经使用<代码> Foo:
Foo foo;

printf( "This is a %s\n", foo.ClassName() );    
    ((x*)0)->f();