C++ C++-&;CRTP。类型擦除与多态性

C++ C++-&;CRTP。类型擦除与多态性,c++,metaprogramming,type-erasure,C++,Metaprogramming,Type Erasure,好的,我们开始吧。我正在尝试使用模板,以便从我的应用程序中删除多态性的需要。我用了一种类似于下面的方法 template <RealType> class Base { void doSomething() { static_cast<RealType>(this)->doSomethingImpl() } class Derived1 : public Base { void doSomethingImpl(

好的,我们开始吧。我正在尝试使用模板,以便从我的应用程序中删除多态性的需要。我用了一种类似于下面的方法

template <RealType> class Base 
{

    void doSomething()
    {
         static_cast<RealType>(this)->doSomethingImpl()
    }

class Derived1 : public Base
{
    void doSomethingImpl()
    {
        /* do something, for real */
    }
}

class Derived2 : public Base
{
    void doSomethingImpl()
    {
        /* do something else */
    }
}
模板类基类
{
无效剂量测定法()
{
静态_cast(this)->doSomethingImpl()
}
类Derived1:公共基
{
void doSomethingImpl()
{
/*做点什么,真的*/
}
}
派生类2:公共基
{
void doSomethingImpl()
{
/*做点别的*/
}
}
如果我理解正确,这个方法允许我的类没有vtable,因此函数调用是直接的,不需要额外的间接寻址

现在假设我想将所有派生类存储在一个容器中,比如向量

第一种方法:我可以创建一个非模板的SuperBase类,Base从中继承并将其存储在容器中

然而,在我看来,如果我想这样做的话,我将不得不在SuperBase中使doSomething虚拟化。我的目标主要是不拥有vtable

第二种方法:我使用类型擦除,即类似boost::any的东西将我的元素存储在向量中。 但是,我看不到一种方法可以迭代元素并对其调用doSomething,因为要“使用”boost::any,我需要在迭代时知道对象的真实类型

你认为我想做的事有可能吗

在我看来,这是因为doSomething()是Base的一部分,但除了使用继承之外,我找不到实现它的方法

我的目标主要是不要有vtable

如果您想要这样做,那么,除非您实现自己的虚拟调度系统(这可能不如编译器所做的),你必须使用模板,也称为编译时多态性。顾名思义,要使用它,一切都必须在编译时知道。如果你需要根据运行时事件(如用户输入)做出决定,你需要运行时多态性


我忍不住要问:为什么要避免vtables?(如果你下定决心,为什么不用C编程?

这会很困难(充其量只是一个黑客)将这些对象保存在容器中。您已经设计了多态性,但您似乎真的想使用它,这样您就可以将对象保存为
容器
,并以多态方式使用它们


从你的帖子中,我不清楚你为什么希望避免使用vtable。如果这是为了性能,你可能是过度优化了。如果没有更多关于你为什么要这样做的背景知识,除了“使用基类”之外,很难推荐任何东西。

你可以通过正确的理论操作来做你想做的事情,而不是多态性,但是统一。大多数人不知道什么是总和类型(歧视性结合)以及它是为了什么,这就是为什么他们总是滥用继承权,这是完全不相关的

在C中,联盟更受欢迎,例如X-Windows事件消息是基于联合的,因为它们在C++中被破坏。 union是将异构数据类型表示为单个类型的正确方法,因此名称统一:它将所有组件统一为单个类型。union总是有有限个已知的组件,使用union的函数总是使用判别式上的开关来选择正确的处理程序代码

OOP不能提供统一:它提供子类型

模板提供了一些完全不同的东西:参数多态性

这三个概念在理论和实践上都是截然不同的。子类型OOP风格被证明是最不有用的,因为它所代表的内容受到了严格的限制,但是这些限制确实允许动态调度到一组开放的子类型,如果它应用于您的问题,这是非常好的

所以现在很清楚,在数组中,您只需要放置所有类的并集,问题就解决了

在C++中,由于无原则的限制,类必须是POD类型。因此,最好的解决方案是使用原始C函数的联合,因为C函数指针是POD。 比如:

enum tag {tag_X, tag_Y};

struct X { int x; };
void px(X a) { cout << a.x; }
struct PX { tag t; X a; void (*p)(X); };

struct Y { double x; };
void py(Y a) { cout << a.x; };
struct PY {tag t; Y a; void (*p)(Y); };

union XY { PX anX; PY anY; };

PX x1 = { tag_X, { 1 }, px };
PY y1 = { tag_Y, { 1.0 }, py };

XY xy1.anPX = x1;
XY xy2.anPy = x2;

XY xys[2] = {xy1, xy1};

xy = xys[0];
switch (xy.anPX.tag) { // hack
  case tag_X: xy.anPX.p (xy.PX.x); break;
  case tag_Y: xy.anPY.p (xy.PY.x); break;
}

在这段代码中,X和Y是上面C代码的标记,它们被称为类型构造函数,因为它们用int或double(resp.)构造xy类型。匹配表达式中只有一个开关,可以自动选择正确的组件类型和作用域,以确保不会引用错误的组件(正如您在C代码中所做的那样),也没有中断,匹配处理程序不会中断,内存管理由垃圾收集器完成。

您可以将这两种功能结合起来

#include <iostream>

struct AnimalBase
{
    virtual std::string speak() const = 0;
    virtual ~AnimalBase() {};
};

template <typename Derived>
struct Animal : AnimalBase
{
    std::string speak() const
    {
        return static_cast<const Derived*>(this)->speak();
    }
};

struct Dog : Animal<Dog>
{
    std::string speak() const
    {
        return "Woof! Woof!";
    }
};

struct Cat : Animal<Cat>
{
    std::string speak() const
    {
        return "Meow. Meow.";
    }
};

int main()
{
    AnimalBase* pets[] = { new Dog, new Cat };

    std::cout << Dog().speak() << '\n'
              << Cat().speak() << '\n'
              << pets[0]->speak() << '\n'
              << pets[1]->speak() << std::endl;

    delete pets[0];
    delete pets[1];
    return 0;
}
#包括
结构生物酶
{
虚拟std::string speak()const=0;
虚拟~AnimalBase(){};
};
模板
结构动物:动物酶
{
std::string speak()常量
{
返回static_cast(this)->speak();
}
};
结构狗:动物
{
std::string speak()常量
{
返回“汪!汪!”;
}
};
结构猫:动物
{
std::string speak()常量
{
返回“喵,喵”;
}
};
int main()
{
AnimalBase*宠物[]={新狗,新猫};

std::我可以从下面的两个答案中总结一点,以后可能会有更多的答案:你说,“我的目标主要是没有vtable”。什么?正常的目标是,“我想写一些能工作的代码”,或者“我想写一些能工作且运行快的代码”.vtables是一个实现细节,是一种提供动态多态性的方法,听起来好像您的主要目标是实现动态多态性。谢谢各位!避免vtables的关键是性能,因为此代码将用于低级函数(即经常调用).一般来说,我并不特别讨厌vtables:)。另一件事是我读了很多关于静态多态性的书,我
#include <iostream>

struct AnimalBase
{
    virtual std::string speak() const = 0;
    virtual ~AnimalBase() {};
};

template <typename Derived>
struct Animal : AnimalBase
{
    std::string speak() const
    {
        return static_cast<const Derived*>(this)->speak();
    }
};

struct Dog : Animal<Dog>
{
    std::string speak() const
    {
        return "Woof! Woof!";
    }
};

struct Cat : Animal<Cat>
{
    std::string speak() const
    {
        return "Meow. Meow.";
    }
};

int main()
{
    AnimalBase* pets[] = { new Dog, new Cat };

    std::cout << Dog().speak() << '\n'
              << Cat().speak() << '\n'
              << pets[0]->speak() << '\n'
              << pets[1]->speak() << std::endl;

    delete pets[0];
    delete pets[1];
    return 0;
}