为什么没有将虚拟静态成员添加为 C++ 的功能?
Why have virtual static members not been added as a feature of C++?
我刚刚读了(第 k 遍)
C++ static virtual members?
这是一个关于模拟虚拟静态成员的问题。我的问题是 - 是什么让 C++ 标准委员会(或之前的 Bjarne Stroustrup)没有将此功能添加到 C?他们知道会破坏任何东西吗?或阻碍任何事物的表现(即使不使用)?
为了更好地说明我对特征定义本身的看法,这里有一些代码:
// 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;
};
注意:我不是在问你的意见或者添加这些是否是个好主意,我是在问历史 和官方 考虑。
虚方法需要一个虚table,一个虚table需要一个带有vtable指针的实例,静态成员方法不通过实例调用,因此它根本不可能。
根据您问题中描述的 "problem",您似乎希望以这种格式使用多态行为:
Everybody::sayHello();
World::sayHello();
但这并没有真正要求多态性,因为您指出了您想要调用的功能类型 - 很明显 Everybody::sayHello()
调用了 Everybody
的功能。没有 "polymorphic ambiguity" - 没有 unknown 类型需要查找其功能以产生预期的多态行为。
因此你并不真的需要动态调度来解决这个问题,你可以简单地使用阴影 - 即使你不能将静态方法重载为虚拟方法,你仍然可以通过阴影重载它们,这没关系,因为您指定类型,因此您将获得正确的版本。
您可以手动隐藏静态方法:
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(); }
};
或使用 class 模板为您完成,因此您只需隐藏 "virtual static method",该模板将确保调用静态方法的正确类型:
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++ 中是不可能的,为什么不需要它,以及为什么在该特定语言中使它成为可能毫无意义。你基本上想在不需要它的场景中使用多态性,为了使语言 "easier" - 好吧,你不能同时拥有它,C++ 很难,因为它很快,只是就像更简单的语言都比 C++ 慢。
最后 - 如果你觉得这是一个非常重要的语言特性 - 你可以随时向标准委员会请求该特性;)
首先,让我们看一个无效示例,说明静态虚拟对象的外观:
// 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
参数携带有关虚函数的信息。但是,当您调用静态函数时,不会传递任何可以识别当前 class 的内容(即上例中的 Hello
与 Everybody
)。没有隐式参数,因此无法访问虚函数。
回到上面的例子,考虑当 Base::sayHello
调用 toWhom()
时会发生什么。它需要一些上下文来决定应该调用三个函数中的哪一个——即 Base::toWhom
、World::toWhom
或 Everybody::toWhom
。不仅缺少此信息,而且在语言中也没有现有机制可以 "piggyback" 此功能,类似于将指向虚函数的指针添加到 [=34= 的数据中的方式] ].
尽管确实可以更改此调用机制,但该语言的作者没有看到这样做的令人信服的理由。
static
成员函数不作用于任何特定对象。
virtual
成员函数是指定行为(调用的函数的版本)取决于所作用对象的类型的方法(例如,object->foo()
执行的代码取决于object
).
的实际类型
这就是 virtual
和 static
这两个概念在 C++ 中的指定方式(至少,在这种情况下)。这两个概念是互斥的。
不可能有不作用于任何对象的运行时行为取决于它作用于的对象的类型。这是一个逻辑谬误。
您可以使用 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 的 SO 上有很多详细的问题和答案。
因为你是在和Objective-C比较:在Objective-C中,class本身就是对象。在 C++ 中,classes 是一个编译时构造;它们在运行时不存在。 C++ 静态 class 方法只是具有一些有趣语法的普通旧外部函数。因为没有 class 不同于 Objective-C 的对象,虚拟静态函数没有任何意义。
我刚刚读了(第 k 遍)
C++ static virtual members?
这是一个关于模拟虚拟静态成员的问题。我的问题是 - 是什么让 C++ 标准委员会(或之前的 Bjarne Stroustrup)没有将此功能添加到 C?他们知道会破坏任何东西吗?或阻碍任何事物的表现(即使不使用)?
为了更好地说明我对特征定义本身的看法,这里有一些代码:
// 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;
};
注意:我不是在问你的意见或者添加这些是否是个好主意,我是在问历史 和官方 考虑。
虚方法需要一个虚table,一个虚table需要一个带有vtable指针的实例,静态成员方法不通过实例调用,因此它根本不可能。
根据您问题中描述的 "problem",您似乎希望以这种格式使用多态行为:
Everybody::sayHello();
World::sayHello();
但这并没有真正要求多态性,因为您指出了您想要调用的功能类型 - 很明显 Everybody::sayHello()
调用了 Everybody
的功能。没有 "polymorphic ambiguity" - 没有 unknown 类型需要查找其功能以产生预期的多态行为。
因此你并不真的需要动态调度来解决这个问题,你可以简单地使用阴影 - 即使你不能将静态方法重载为虚拟方法,你仍然可以通过阴影重载它们,这没关系,因为您指定类型,因此您将获得正确的版本。
您可以手动隐藏静态方法:
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(); }
};
或使用 class 模板为您完成,因此您只需隐藏 "virtual static method",该模板将确保调用静态方法的正确类型:
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++ 中是不可能的,为什么不需要它,以及为什么在该特定语言中使它成为可能毫无意义。你基本上想在不需要它的场景中使用多态性,为了使语言 "easier" - 好吧,你不能同时拥有它,C++ 很难,因为它很快,只是就像更简单的语言都比 C++ 慢。
最后 - 如果你觉得这是一个非常重要的语言特性 - 你可以随时向标准委员会请求该特性;)
首先,让我们看一个无效示例,说明静态虚拟对象的外观:
// 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
参数携带有关虚函数的信息。但是,当您调用静态函数时,不会传递任何可以识别当前 class 的内容(即上例中的 Hello
与 Everybody
)。没有隐式参数,因此无法访问虚函数。
回到上面的例子,考虑当 Base::sayHello
调用 toWhom()
时会发生什么。它需要一些上下文来决定应该调用三个函数中的哪一个——即 Base::toWhom
、World::toWhom
或 Everybody::toWhom
。不仅缺少此信息,而且在语言中也没有现有机制可以 "piggyback" 此功能,类似于将指向虚函数的指针添加到 [=34= 的数据中的方式] ].
尽管确实可以更改此调用机制,但该语言的作者没有看到这样做的令人信服的理由。
static
成员函数不作用于任何特定对象。
virtual
成员函数是指定行为(调用的函数的版本)取决于所作用对象的类型的方法(例如,object->foo()
执行的代码取决于object
).
这就是 virtual
和 static
这两个概念在 C++ 中的指定方式(至少,在这种情况下)。这两个概念是互斥的。
不可能有不作用于任何对象的运行时行为取决于它作用于的对象的类型。这是一个逻辑谬误。
您可以使用 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 的 SO 上有很多详细的问题和答案。
因为你是在和Objective-C比较:在Objective-C中,class本身就是对象。在 C++ 中,classes 是一个编译时构造;它们在运行时不存在。 C++ 静态 class 方法只是具有一些有趣语法的普通旧外部函数。因为没有 class 不同于 Objective-C 的对象,虚拟静态函数没有任何意义。