我正在使用提供消息的PolyM message queue
class Msg
以及带有模板有效负载的消息
template<typename PayloadType> class DataMsg: public Msg
这一直有效,直到我将DataMsg模板嵌套在另一个DataMsg中,就像这样...
DataMsg<DataMsg<int>>
并尝试提取嵌套的DataMsg,以将其传递给进一步处理。为此,我将Msg基本类型转换为:
function(Msg &base) {
auto &msg = dynamic_cast<DataMsg<Msg>&>(base).getPayload();
}
此转换因错误的转换异常而失败。相反,使用static_cast似乎没有任何副作用。
从多态的观点来看,我的方法没有发现任何问题。由于动态类型转换适用于非嵌套类型,它也应该适用于嵌套类型吗?
我已经在PolyM GitHub issues page上问了这个问题,但是并没有得到正确的解释,说明强制转换失败的原因。
这是一个显示问题的简约示例:
#include <memory>
#include <iostream>
using namespace std;
class Msg {
public:
virtual ~Msg() {}
};
template<typename PayloadType>
class DataMsg: public Msg {
public:
virtual ~DataMsg() {}
PayloadType& getPayload() const
{
return *pl_;
}
private:
PayloadType* pl_;
};
static void getInnerMsg(Msg &msgMsg) {
try {
auto &msg = dynamic_cast<DataMsg<Msg>&>(msgMsg).getPayload();
std::cout << "cast OK" << endl;
} catch ( std::bad_cast& bc ) {
std::cerr << "bad_cast caught: " << bc.what() << endl;
}
}
和我的测试用例:
int main(int argc, char *argv[])
{
Msg msg1;
DataMsg<int> msg2;
DataMsg<Msg> msg3;
DataMsg<DataMsg<int>> msg4;
cout << "expect bad cast (no nested message)" << endl;
getInnerMsg(msg1);
cout << "-------------" << endl;
cout << "expect bad cast (no nested message)" << endl;
getInnerMsg(msg2);
cout << "-------------" << endl;
cout << "expect successful cast (nested message base type)" << endl;
getInnerMsg(msg3);
cout << "-------------" << endl;
cout << "expect successful cast (nested message child type)" << endl;
getInnerMsg(msg4);
return 0;
}
运行“ g ++ test.cpp -o test.x && ./test.x”。 GitHub问题包含一个更完整的用法示例。
答案 0 :(得分:0)
(简短地)在github问题答复中进行了解释:
function(Msg &base) {
auto &msg = dynamic_cast<DataMsg<Msg>&>(base).getPayload();
}
您在这里尝试从Msg
投射到DataMsg<Msg>
。但是,您指示base
的动态类型为DataMsg<DataMsg<int>>
。 DataMsg<Msg>
继承自Msg
,DataMsg<DataMsg<int>>
继承自Msg
,但它们之间是无关的(无论模板参数之间的关系如何)。
更笼统地说,MyClass<Derived>
并不继承自MyClass<Base>
-(无论两者是否都源自同一基类),因此动态地从一个或另一个强制转换另一个是非法的(通过/从是否有共同基础。
答案 1 :(得分:0)
从多态的观点来看,我的方法没有发现任何问题。由于动态类型转换适用于非嵌套类型,它也应该适用于嵌套类型吗?
关于模板,即使A<B>
和A<C>
是相关的,B
和C
也是不同的不相关类型。使用通过static_cast
获得的引用会导致未定义的行为。为了说明这一点,如果我们要手写您得到的类层次结构,它将类似于此
struct base { ~virtual base() = default; };
struct foo : base {
base *p;
};
struct bar : base {
foo *p;
};
在上面的示例中,如果对象的动态类型为bar
,则不能将其强制转换为foo&
,这些类型彼此之间不在继承链中,并且动态转换将会失败。但是从static_cast
(指base&
)到bar
的{{1}}将会成功。没有运行时检查,编译器会随便告诉您有关类型的信息,但您并没有说出真相。如果使用该引用,则会发生未定义的行为。可悲的是,“工作”的出现是未定义行为的有效体现。
答案 2 :(得分:0)
这一直有效,直到我将DataMsg模板嵌套在另一个DataMsg中...尝试提取嵌套的DataMsg
到目前为止,您的逻辑对于拆开嵌套邮件具有直观的意义。
您有一条带有通用标头的通用消息,该标头包含某种特定类型的动态具体消息,提取特定的消息有效负载,其中包含另一种特定类型的其他消息,等等。
一切都很好。
您的概念性问题是您正在处理嵌套模板参数(例如嵌套消息对象),而它们根本不一样。
template<typename PayloadType> class DataMsg: public Msg
// ...
private:
PayloadType* pl_;
};
表示DataMsg
模板 is-a Msg
的每个实例,并且具有指向所包含有效负载的指针。
现在,DataMsg
的每个实例都是新的类型DataMsg<X>
,即使DataMsg<Y>
和X
相关,它也与任何其他实例Y
无关(除了它们均来自Msg
)。所以:
DataMsg<DataMsg<int>>
is-a Msg
和 has-a 指向DataMsg<int>
的指针(本身是 is-a Msg
),而
DataMsg<Msg>
也是 is-a Msg
和 has-a 指向Msg
的指针,但仍然是与{{ 1}}(除非具有通用基数),即使有效载荷指针类型是可转换的。
因此,关于消息布局的想法很好,但是您没有在类型系统中正确地对其建模。如果要执行此操作,则可以显式地专门处理嵌套消息:
DataMsg<DataMsg<int>>
现在using NestedMsg = DataMsg<Msg>;
template<typename NestedPayloadType>
class DataMsg<DataMsg<NestedPayloadType>>: public NestedMsg {
public:
NestedPayloadType const & getDerivedPayload() const
{
// convert nested Msg payload to derived DataMsg
return dynamic_cast<DataMsg<NestedPayloadType> const &>(this->getPayload());
}
};
确实是 a DataMsg<DataMsg<int>>
。这样,它继承了DataMsg<Msg>
,并且您仍然可以通过调用`getDerivedPayload()获取负载的派生类型(Msg const& DataMsg<Msg>::getPayload() const
)。
您的其他DataMsg<int>
方法也应返回const引用BTW。