Why have virtual static members not been added as a feature of C++?
我刚读(第k次)
C++静态虚拟成员?
这是一个关于模拟虚拟静态成员的问题。我的问题是,什么使C++标准委员会(或之前的Bjarne Stroustrup)不将这个特性添加到C?他们知道会破坏什么吗?或者妨碍任何东西的性能(即使不使用)?
为了更好地说明我将要接管的功能定义本身,下面是一些代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // This does not compile! class Base { // A pure virtual member - perhaps need to indicate that somehow virtual static const string ToWhom; void sayHello() { cout <<"Hello," << ToWhom <<"!" << endl; } }; class World : public Base { static virtual const string ToWhom ="the entire world"s; // C++14 literal }; class Everybody : public Base { static virtual const string ToWhom ="everybody around"s; }; |
注:我不是在问你的意见,也不是在问增加这些是不是一个好主意,我是在问历史和官方的考虑。
首先,让我们看一个静态虚拟的无效示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // WARNING: This does not compile !!! class Base { static virtual string toWhom() { return"unknown"; } static void sayHello() { cout <<"Hello," << toWhom() <<"!" << endl; } }; class World : public Base { static virtual string toWhom() { return"world"; } }; class Everybody : public Base { static virtual string toWhom() { return"everybody"; } }; |
这样可以做到:
1 2 3 | // WARNING: This does not work !!! Everybody::sayHello(); // Prints"Hello, everybody!" World::sayHello(); // Prints"Hello, world!" |
然而,问题是这样的调度在不改变C++中静态函数调用方式的情况下是不容易实现的。
回想一下,非静态成员函数隐式地获取
回到上面的例子,考虑当
虽然这种调用机制确实可以更改,但是语言的作者没有看到这样做的令人信服的原因。
一个虚拟方法需要一个虚拟表,一个虚拟表需要一个带有vtable指针的实例,静态成员方法不能通过实例调用,因此这是不可能的。
从您问题中描述的"问题"来看,您可能期望使用这种格式的多态行为:
1 2 | Everybody::sayHello(); World::sayHello(); |
但这并不要求多态性,因为你指出了你想要调用的功能类型——很明显,
因此,您不需要动态调度来解决这个问题,您可以简单地使用阴影-即使您不能将静态方法作为虚拟方法重载,您仍然可以通过阴影来重载它们,这是可以的,因为您指定了类型,因此您将获得正确的版本。
您可以手动隐藏静态方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | struct Base { static string toWhom() { return""; } static void sayHi() { cout <<"Hello" + toWhom(); } }; struct World : Base { static string toWhom() { return"World"; } static void sayHi() { cout <<"Hello" + toWhom(); } }; struct Everyone : Base { static string toWhom() { return"Everyone"; } static void sayHi() { cout <<"Hello" + toWhom(); } }; |
或者使用一个类模板来完成它,因此您只需隐藏"虚拟静态方法",模板将确保为以下对象调用静态方法的正确类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 | template <typename T> struct Base { static string toWhom() { return""; } static void sayHi() { cout <<"Hello" + T::toWhom(); } }; struct World : Base<World> { static string toWhom() { return"World"; } }; struct Everyone : Base<Everyone> { static string toWhom() { return"Everyone"; } }; |
然后
1 2 | Everybody::sayHello(); World::sayHello(); |
这两种解决方案都将产生预期的结果。实现这一目标根本不需要任何多态性。请注意,确实可以实现您想要的,但这只会让您有可能创建一个效率较低的解决方案——因为多态性既有内存又有CPU时间开销,C++是一个主要关注性能和效率的语言。因此,它不支持不需要的特性,因为它已经可以按您的要求执行,而且它将很快燃烧起来,因为这样的简单函数甚至不会被调用——它们将被内联。内联函数和调用虚拟方法(比如对于这样的小函数使用20x)之间存在巨大的性能差异,并且为了实现静态虚拟成员而添加另一个间接级别只会使其更糟。
我希望现在我已经给出了令人信服的答案,为什么这是不可能的C++,为什么它不需要,以及为什么它没有意义使它成为可能在特定的语言。你基本上想在一个不需要它的情况下使用多态性,为了使语言变得更简单——嗯,你不能同时拥有它,C++是很难的,因为它很快,就像更容易的语言都比C++慢。
最后——如果你觉得这是一个非常重要的语言特性——你可以向标准委员会请求这个特性;)
您可以使用CRTP静态地实现多态行为。例如,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include <iostream> using namespace std; template <typename Derived> struct Base { static void sayHello() { cout <<"Hello," << Derived::toWhom() <<"!" << endl; } }; struct World : public Base<World> { static string toWhom() { return"world"; } }; struct Everybody : public Base<Everybody> { static string toWhom() { return"everybody"; } }; int main() { World::sayHello(); Everybody::sayHello(); return 0; } |
如果你想了解更多关于CRTP的信息,有很多关于CRTP的详细问题和答案。
因为您要与Objective-C进行比较:在Objective-C中,类本身就是对象。在C++中,类是编译时构造;它们在运行时不存在。C++静态类方法只是普通的外部函数,有一些有趣的语法。因为没有与Objective-C不同的类对象,所以虚拟静态函数没有任何意义。
这就是EDOCX1、1和EDCOX1(0)的两个概念是如何在C++中指定的(至少在这种情况下)。这两个概念相互排斥。
不可能有不作用于任何对象的运行时行为,这取决于它作用于的对象类型。这是一个合乎逻辑的谬论。