Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/130.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 给定抽象基类X,如何创建另一个模板类D<;T>;其中T是从X派生的类的类型?_C++_Templates_Inheritance_Double Dispatch - Fatal编程技术网

C++ 给定抽象基类X,如何创建另一个模板类D<;T>;其中T是从X派生的类的类型?

C++ 给定抽象基类X,如何创建另一个模板类D<;T>;其中T是从X派生的类的类型?,c++,templates,inheritance,double-dispatch,C++,Templates,Inheritance,Double Dispatch,我希望能够接受引用Message1或Message2类的消息&对象。我希望能够基于Message&对象的基础类型创建MessageWithData或MessageWithData。例如,请参见以下内容: class Message {}; class Message1 : public Message {}; class Message2 : public Message {}; template<typename Message1or2> class MessageWithDat

我希望能够接受引用
Message1
Message2
类的
消息&
对象。我希望能够基于
Message&
对象的基础类型创建
MessageWithData
MessageWithData
。例如,请参见以下内容:

class Message {};
class Message1 : public Message {};
class Message2 : public Message {};

template<typename Message1or2>
class MessageWithData : public Message1or2 { public: int x, y; }

class Handler()
{
public:
  void process(const Message& message, int x, int y)
  {
    // create object messageWithData whose type is 
    // either a MessageWithData<Message1> or a MessageWithData<Message2> 
    // based on message's type.. how do I do this?
    //
    messageWithData.dispatch(...)
  }
};
类消息{};
类Message1:公共消息{};
类Message2:公共消息{};
模板
类MessageWithData:public Message1or2{public:int x,y;}
类处理程序()
{
公众:
无效流程(常量消息和消息,整数x,整数y)
{
//创建对象messageWithData,其类型为
//MessageWithData或MessageWithData
//基于消息的类型..如何执行此操作?
//
messageWithData.dispatch(…)
}
};
messageWithData类本质上包含从Message继承的方法,这些方法允许根据其类型将其动态双重调度回处理程序。到目前为止,我最好的解决方案是将数据与消息类型分开,并通过动态调度链传递数据,但我希望更接近动态双重调度的真正用法,即消息类型包含变量数据


(我或多或少遵循的方法来自)

您正在尝试混合运行时和编译时概念,即(运行时)多态性和模板。对不起,那是不可能的

模板在编译时对类型进行操作,也称为静态类型
message
的静态类型是
message
,而其动态类型可能是
Message1
Message2
。模板对动态类型一无所知,无法对其进行操作。使用运行时多态性或编译时多态性,有时也称为静态多态性

运行时多态性方法是访问者模式,具有双重分派。以下是编译时多态性的示例,使用:

模板
类消息{};
类Message1:公共消息{};
类Message2:公共消息{};
模板
类MessageWithData:publictmessage{public:intx,y;};
类处理程序{
公众:
模板
无效流程(消息常量&m,整数x,整数y){
数据随钻测量;
mwd.x=42;
mwd.y=1337;
}
};
您有

void process(const Message& message, int x, int y)
{
  // HERE
  messageWithData.dispatch(...)
}
在这里,您需要创建
MessageWithData
MessageWithData
,这取决于
message
Message1
还是
Message1
的实例


但是您不能这样做,因为类模板
MessageWithData
需要在编译时知道什么是
T
,但是在运行时通过调度到
message

,该类型在代码中不可用,正如Xeo所说,在这种特殊情况下,您可能不应该这样做-存在更好的设计备选方案。这就是说,您可以使用RTTI来实现这一点,但人们通常不赞成这样做,因为您的
process()
成为一个集中维护点,需要在添加新的派生类时进行更新。这很容易被忽略,并且容易出现运行时错误

如果出于某种原因必须使用此功能,那么至少要对该功能进行概括,以便单个函数使用基于RTTI的运行时类型确定来调用任意行为,如:

#include <iostream>
#include <stdexcept>

struct Base
{
    virtual ~Base() { }

    template <class Op>
    void for_rt_type(Op& op);
};

struct Derived1 : Base
{
    void f() { std::cout << "Derived1::f()\n"; }
};

struct Derived2 : Base
{
    void f() { std::cout << "Derived2::f()\n"; }
};

template <class Op>
void Base::for_rt_type(Op& op)
{
    if (Derived1* p = dynamic_cast<Derived1*>(this))
        op(p);
    else if (Derived2* p = dynamic_cast<Derived2*>(this))
        op(p);
    else
        throw std::runtime_error("unmatched dynamic type");
}

struct Op
{
    template <typename T>
    void operator()(T* p)
    {
        p->f();
    }
};

int main()
{
    Derived1 d1;
    Derived2 d2;
    Base* p1 = &d1;
    Base* p2 = &d2;
    Op op;
    p1->for_rt_type(op);
    p2->for_rt_type(op);
}
#包括
#包括
结构基
{
虚拟~Base(){}
模板
对于_rt_类型无效(Op&Op);
};
结构Derived1:Base
{
void f(){std::cout for_rt_type(op);
p2->对于rt类型(op);
}
在上面的代码中,您可以替换自己的Op,并使用相同的运行时进行compiletime切换。将其视为工厂方法(反向:-})可能会有帮助,也可能不会有帮助

如前所述,必须为每个派生类型更新\u rt\u type的
:如果一个团队“拥有”基类,而其他团队编写派生类,那么这尤其令人痛苦。与许多稍显粗糙的东西一样,它在支持私有实现方面比作为低级企业库的API特性更加实用和可维护。想要使用它通常仍然是其他地方设计不好的标志,但并不总是这样:偶尔会有算法(
Op
s)受益匪浅:

  • 编译时优化、死代码删除等
  • 派生类型只需要相同的语义,但细节可能会有所不同
    • e、 g.
      Derived1::value_type
      int
      Derived2::value_type
      double
      -允许每种算法有效并使用适当的舍入等。。类似地,对于仅使用共享API的不同容器类型
  • 您可以使用模板元编程、SFINAE等以派生类型特定的方式自定义行为

就我个人而言,我认为掌握这种技术的知识和能力(尽管很少)是掌握多态性的一个重要部分。

如前所述,不可能按原样构建模板

我认为传递附加参数没有任何问题,尽管为了便于操作,我可能会将它们打包到单个结构中

当然,我发现使用一个补充的
Data
参数比扩展一个类层次结构来将所有这些嵌入到一个模式中更为惯用

试图使设计符合模式是一种反模式。正确的方法是调整图案,使其适合设计

话虽如此


您的解决方案有几种替代方案。继承看起来很奇怪,但如果手头没有整个设计,它可能是你最好的选择

已经提到,您不能自由地混合编译时和
#include <iostream>
#include <stdexcept>

struct Base
{
    virtual ~Base() { }

    template <class Op>
    void for_rt_type(Op& op);
};

struct Derived1 : Base
{
    void f() { std::cout << "Derived1::f()\n"; }
};

struct Derived2 : Base
{
    void f() { std::cout << "Derived2::f()\n"; }
};

template <class Op>
void Base::for_rt_type(Op& op)
{
    if (Derived1* p = dynamic_cast<Derived1*>(this))
        op(p);
    else if (Derived2* p = dynamic_cast<Derived2*>(this))
        op(p);
    else
        throw std::runtime_error("unmatched dynamic type");
}

struct Op
{
    template <typename T>
    void operator()(T* p)
    {
        p->f();
    }
};

int main()
{
    Derived1 d1;
    Derived2 d2;
    Base* p1 = &d1;
    Base* p2 = &d2;
    Op op;
    p1->for_rt_type(op);
    p2->for_rt_type(op);
}
class Message {};
template <typename T> class MessageShim<T>: public Message {};
class Message1: public MessageShim<Message1> {};