Why have virtual static members not been added as a feature of C++?

时间:2015-04-24 21:30:25

标签: c++ virtual static-methods static-members

I just read (for the k'th time)

C++ static virtual members?

Which is a question about simulating virtual static members. My question is - what made the C++ standards committe (or Bjarne Stroustrup before that) not add this feature to C? Are they known to break anything? Or impede the performance of anything (even when not used)?

To better illustrate what I'm taking about over the feature definition itself, here is some code:

// 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;
};

Note: I'm not asking about your opinion or whether adding these is a good idea, I'm asking about the history and the official considerations.

5 个答案:

答案 0 :(得分:7)

首先,让我们看一下静态虚拟外观的无效示例:

// 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";
    }
};

这可以让你这样做:

// WARNING: This does not work !!!
Everybody::sayHello(); // Prints "Hello, everybody!"
World::sayHello(); // Prints "Hello, world!"

然而,问题是如果不改变在C ++中调用静态函数的方式,这样的调度就不容易实现。

回想一下,非静态成员函数隐式地获取this参数。它是this参数,用于携带有关虚函数的信息。但是,当您调用静态函数时,不会传递任何可识别当前类的内容(即上例中的HelloEverybody。没有隐式参数,因此无法访问虚函数。

回到上面的示例,考虑Base::sayHello调用toWhom()时会发生什么。它需要有一些上下文来决定应该调用三个函数中的哪一个 - 即Base::toWhomWorld::toWhomEverybody::toWhom。这些信息不仅缺失了,而且我们还没有现成的语言机制,可以捎带&#34;此功能的方式类似于将指向虚拟函数的指针添加到类的数据中。

虽然这种调用机制确实可以改变,但该语言的作者没有看到令人信服的理由。

答案 1 :(得分:4)

虚方法需要虚拟表,虚拟表需要带有vtable指针的实例,静态成员方法不能通过实例调用,因此根本不可能。

从你问题中描述的“问题”来看,似乎你会期望以这种格式使用多态行为:

Everybody::sayHello();
World::sayHello(); 

但这并没有真正要求多态性,因为你指出了你想要调用的功能 - 很明显Everybody::sayHello()会调用Everybody的功能。没有“多态模糊” - 没有未知类型,需要查找其功能以产生预期的多态行为。

因此你真的不需要动态调度来解决这个问题,你可以简单地使用阴影 - 即使你不能将静态方法作为虚方法重载,你仍然可以通过遮蔽它们来重载,并且没关系,因为你指定了因此,您将获得正确的版本。

您可以手动遮蔽静态方法:

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

或者使用类模板为您完成它,因此您只需要隐藏“虚拟静态方法”,模板将确保调用静态方法的正确类型:

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"; }
};

然后

Everybody::sayHello();
World::sayHello(); 

两种解决方案都会产生预期的结果。根本不需要任何多态来实现这一目标。请注意,确实可以实现您想要的内容,但这只会让您创建一个效率较低的解决方案 - 因为多态性同时具有内存和CPU时间开销,而C ++是一种主要关注性能和语言的语言。效率。因此它不支持不需要的功能,因为它已经可以完成你所要求的功能,而且速度非常快,因为甚至不会调用这些简单的函数 - 它们将被内联。内联函数和虚拟方法调用之间存在巨大的性能差异(对于这些简单的函数,如20x),并且为了实现静态虚拟成员而添加另一级别的间接将只会让事情变得更糟

我希望现在我已经给出了令人信服的答案,为什么这在C ++中是不可能的,为什么不需要它,以及为什么用这种特定的语言使它成为可能是没有意义的。你基本上想在不调用它的场景中使用多态,为了使语言“更容易” - 好吧,你不能两种方式,C ++很难,因为它很快,就像更容易语言都比C ++慢。

最后 - 如果您觉得这是一个非常重要的语言功能 - 您可以随时向标准委员会索取该功能;)

答案 2 :(得分:1)

您可以使用CRTP静态实现多态行为。例如,

#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的详细问题和答案。

答案 3 :(得分:0)

static成员函数不会对任何特定对象起作用。

virtual成员函数是指定行为(被调用函数的版本)取决于被操作对象类型的方法(例如object->foo()执行的代码取决于实际类型object)。

这就是如何在C ++中指定virtualstatic的两个概念(至少在此上下文中)。这两个概念是互斥的。

不可能使运行时行为不依赖于它所作用的对象类型而对任何对象起作用。这是一个合乎逻辑的谬误。

答案 4 :(得分:0)

由于您正在与Objective-C进行比较:在Objective-C中,类本身就是对象。在C ++中,类是编译时构造;它们在运行时不存在。 C ++静态类方法只是普通的旧外部函数,有一些有趣的语法。因为没有与Objective-C不同的类对象,所以虚拟静态函数没有任何意义。