为什么C++不允许您请求指向最派生类的指针?

Mat*_*owe 5 c++ inheritance multiple-dispatch rtti dynamic-dispatch

(这个问题应该通过对Stroustrup的引用来回答.)

能够请求指向最派生类的指针似乎非常有用,如下所示:

class Base { ... };
class DerivedA { ... };
class DerivedB { ... };
class Processor
{
  public:
  void Do(Base* b) {...}
  void Do(DerivedA* d) {...}
  void Do(DerivedB* d) {...}
};

list<Base*> things;
Processor p;
for(list<Base*>::iterator i=things.begin(), e=things.end(); i!=e; ++i)
{
    p.Do(CAST_TO_MOST_DERIVED_CLASS(*i));
}
Run Code Online (Sandbox Code Playgroud)

但是c ++中没有提供这种机制.为什么?

更新,激励示例:

假设您没有Base和Derived and Processor,而是拥有:

class Fruit
class Apple : public Fruit
class Orange: public Fruit

class Eater
{
   void Eat(Fruit* f)  { ... }
   void Eat(Apple* f)  { Wash(f); ... }
   void Eat(Orange* f) { Peel(f); ... }
};

Eater me;
for each Fruit* f in Fruits
    me.Eat(f);
Run Code Online (Sandbox Code Playgroud)

但这在C++中很棘手,需要像访问者模式这样的创造性解决方案.那么问题是:为什么在C++中这样做很棘手,像"CAST_TO_MOST_DERIVED"这样的东西会让它变得更简单?

更新:维基百科知道全部

我认为Pontus Gagge有一个很好的答案.从多重调度的维基百科条目中添加它:

"Stroustrup提到他喜欢C++设计和演变中的多方法概念,并考虑用C++实现它,但声称无法找到有效的示例实现(与虚函数相比)并解决了一些可能的类型模糊问题他继续说明虽然该功能仍然很好,但它可以使用双调度或基于类型的查找表来近似实现,如上面的C/C++示例中所述,因此是未来语言的低优先级功能修订版".

对于背景,你可以阅读一些关于多方法的摘要,这比我提到的那个更好,因为它们只是起作用.

Tro*_*our 8

可能是因为这就是虚拟功能为您所做的事情.当您通过基类指针或引用调用它时,将调用最接近最派生类的虚函数的实现.


AnT*_*AnT 8

首先,C++允许您以数字术语(即只是地址的数值)请求指向最派生类的指针.这是什么dynamic_castvoid*呢.

其次,没有办法获得指向最派生类的精确类型的最派生类的指针.在C++中,强制转换使用静态类型,静态类型是编译时概念.基于类型的函数重载也是一个编译时进程.在您的情况下,在编译时不知道确切的派生类型,这就是为什么不能转换为它并且无法解决它的重载.在C++语言领域,拥有这样一个演员的请求毫无意义.

你想要实现的(如果我理解你的意图正确),是通过完全不同的方式实现的,而不是通过演员实现的.举一个例子,阅读有关双重派遣的内容.


sto*_*tal 6

因为i的类型在编译时是不可确定的.因此编译器不知道要生成哪个函数调用.C++仅支持一种动态分派方法,即虚函数机制.


Pon*_*gge 4

您的建议相当于switch运行时类型,调用重载函数之一。正如其他人所指出的,您应该使用继承层次结构,而不是反对它:在类层次结构中使用虚拟而不是在其外部分派。

\n\n

也就是说,这样的东西对于双重调度可能很有用,特别是如果您还有一个层次结构Processors。但编译器将如何实现它呢?

\n\n

首先,您必须在运行时提取所谓的“最重载类型”。这是可以做到的,但是您将如何处理多重继承和模板等问题?语言中的每个功能都必须与其他功能良好地交互——而 C++ 有大量的功能!

\n\n

其次,为了让您的代码示例正常工作,您必须根据运行时类型获得正确的静态重载(C++ 的设计不允许这样做)。您希望它遵循编译时查找规则,尤其是多个参数吗?您是否希望此运行时调度也考虑Processor层次结构的运行时类型,以及它们添加了哪些重载?您希望编译器自动添加多少逻辑到您的运行时调度程序中?您将如何处理无效的运行时类型?该功能的用户是否意识到看似简单的转换和函数调用的成本和复杂性?

\n\n

总而言之,我\xc2\xb4d说该功能实现起来很复杂,在实现和使用中都容易出现错误,并且仅在极少数情况下有用。

\n