为什么静态成员函数定义不能使用关键字“static”?

22
根据C++中关于'static'关键字的此链接:静态关键字只能在类定义内部声明静态成员时使用,但不能与该静态成员的定义一起使用。
为什么静态关键字在成员函数定义中被禁止使用?我明白重新声明函数为“static”在其定义中是多余的。但是,在函数定义的编译过程中使用它应该是无害的,因为它不会导致任何类型的歧义。所以为什么编译器禁止它?

5
在类外定义函数时,加上 static 关键字意味着该函数具有 内部链接,但是对于类成员函数而言,则具有 外部链接。这可能会引起混淆。 - M.M
编译器始终处于一种状态,可以确定一个函数是成员函数还是全局函数。对它来说,这应该不会太令人困惑。:-/ - johngreen
@M.M但他正在谈论类成员函数。 - Jabberwocky
@MichaelWalz 不开玩笑 - M.M
3
对于人来说,阅读这段代码可能会有些困惑,但对编译器来说则没有问题。 - M.M
4个回答

20

确实存在歧义。相同的定义根本不一定适用于成员函数。

考虑这个例子:

namespace foo {
    static void bar();
}

static void foo::bar() {

}

foo::bar 必须使用相同的链接说明符定义。

然而对于成员函数,static 不是一个链接说明符。如果允许,foo::bar 的定义正确性将非常取决于foo 是什么。事实上,不允许 static 可以减轻编译器的负担。

将其扩展到一般成员,而不仅仅是成员函数,是为了保持一致性。


Delphi将这些函数称为“类”函数,而不是“静态”函数,使用class关键字来表示它们。回过头来看,对于C++而言,这可能是一个更好的想法,而不是重新使用“static”来表示完全不同的东西... - Mark K Cowan
@MarkKCowan - "Overloading" 一个关键字(而且是从C继承而来的)并不是最好的想法,我同意。我不确定使用 class 是否更好(部分原因是结构体和类是可互换的,那么怎么办?一个“结构体”成员函数?)。也许一个完全不同的关键字会更好。这样编译器就可以比它们今天所处的上下文依赖性更少。 - StoryTeller - Unslander Monica
类已经针对模板参数进行了重载,其中“struct”不能代替它,即使我们可以为结构体创建模板。 - Mark K Cowan
我已经接受了这个答案,因为它提供了编译器想要强制执行这个规则的一个很好的理由。 - johngreen
@MarkKCowan - 但这并不是一个明显的不匹配情况。如果您使用struct关键字定义类型,用"class"指定函数会相当奇怪。至少在我看来是这样。 - StoryTeller - Unslander Monica

5
重点是,static有几个非常不同的含义:
class Foo {
    static void bar();
}

这里的static关键字意味着函数bar与类Foo相关联,但它不是在Foo实例上调用的。这种static的含义与面向对象编程密切相关。然而,声明方式为:
static void bar();

“static”在IT技术中有着不同的含义:它意味着bar仅在文件范围内可见,函数无法直接从其他编译单元调用。

你看,如果你在类声明中使用static,那么限制函数作用于文件范围将毫无意义。而如果你有一个static函数(文件作用域),那么将其发布为公共头文件的类定义的一部分也就没有意义了。这两种意义是如此不同,以至于它们几乎互相排斥。


static甚至有更多的不同含义:

void bar() {
    static int hiddenGlobal = 42;
}

这里有另一种含义,类似但不完全相同

class Foo {
    static int classGlobal = 6*7;
}

在编程中,单词并不总是在所有情境下都有相同的含义。

这个问题并不涉及到C++中'static'的不同含义。作为一个合格的C++程序员,我了解所有这些含义。 - johngreen
@aakashbhowmick 但是你问为什么编译器不允许你结合这两个不同的含义,似乎认为“static”在两种情况下具有相同的效果。当你在类范围之外的函数上使用“static”时,它与在类范围内使用它时完全不同。 - cmaster - reinstate monica
我非常清楚静态成员函数与静态全局函数的区别。我的问题是为什么静态成员函数的定义不应该包含关键字“static”,尽管同一函数的声明可以(而且应该)包含。 - johngreen
@AAkashbhowmick 嗯,从语法上讲,静态成员函数定义和文件作用域函数定义之间没有区别。这两个定义之间唯一的区别是,成员函数定义通常需要使用作用域解析运算符。但是,如果它是命名空间的一部分,则该运算符也可以出现在文件作用域函数中,因此这不是语法上的区别,而仅仅是语义上的一个区别。 - cmaster - reinstate monica

1
你需要理解声明和实现的区别,这将回答你的问题:
声明:是指在编译程序之前 C++ 函数和方法的定义。它们被放在头文件(.h 文件)中。
实现:是指编译器将声明链接到二进制代码中的实际任务。实现可以即时编译(从源文件,.cpp 或 .cxx 或 .cc),也可以已经编译好了(从共享库或对象文件)。
现在回到你的问题,当你将某些东西声明为静态时,它与实现无关,但与编译器在编译代码时如何看待声明有关。例如,如果你在源文件中标记函数为“static”,那么那将是没有意义的,因为该信息无法传递到编译后的对象和共享库中。为什么允许它?相反,它只会引起歧义。
出于完全相同的原因,默认参数必须放在头文件中,而不是源文件中。因为源文件(包含实现)无法将默认参数信息传递到编译后的对象中。

0
猜测一下,如果定义中有static关键字,它可以被解释为C语言意义上的文件作用域变量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接