[英]Is it possible to automatically cast to most derived type?
我编写了一个内存扫描应用程序,其中的“模式”由任意模板化的“单元”组成。 单元类也派生自非模板化父包装器类(因此我可以将单元指针存储在向量中)。
如何显示不在父类中的模板化单元成员而无需手动向下转换?
我想做类似auto unitT = most_derived_cast(unitW);
这是示例代码:
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Wrapper {
public:
virtual void* get1() const = 0;
};
template<typename T>
class Derived : public Wrapper {
T m_value;
public:
Derived(T value) : m_value{ value } {}
void* get1() const override {
return new T(m_value);
}
T get2() const {
return m_value;
}
};
int main(int argc, char** argv) {
vector<shared_ptr<Wrapper>> wrapped;
for (int i = 0; i < 10; i++) {
wrapped.emplace_back(new Derived<int>(i));
wrapped.emplace_back(new Derived<float>(i));
}
for (const shared_ptr<Wrapper>& w : wrapped) {
cout << *w->get1() << " "; // bummer; can't dereference from void*
auto v = w->get2(); // bummer; w is of type Wrapper
if (dynamic_pointer_cast<Derived<int>>(w)) { // bummer; have to manually cast
cout << *(int*)w->get1() << " ";
cout << dynamic_pointer_cast<Derived<int>>(w)->get2() << " ";
}
else if (dynamic_pointer_cast<Derived<float>>(w)) {
cout << *(float*)w->get1() << " ";
cout << dynamic_pointer_cast<Derived<float>>(w)->get2() << " ";
}
else {
cout << "bummer "; // bummer; forgot to implement this...
}
}
system("PAUSE");
return 0;
}
C ++是否可以自动转换为大多数派生类型?
不可以。 通常,当人们认为自己需要此功能时,他们需要的是虚拟功能。
如何显示不在父类中的模板化单元成员而无需手动向下转换?
解决方案示例:
添加一个新的虚拟函数以插入流中:
class Wrapper {
public:
virtual std::ostream&
stream_insert(std::ostream& os) const = 0;
// ...
};
使Wrapper
流式传输,并委派给虚函数:
std::ostream&
operator<<(std::ostream& os, const Wrapper& w) {
return w.stream_insert(os);
}
在派生类中实现该功能:
std::ostream&
stream_insert(std::ostream& os) const override {
return os << m_value;
}
现在,您可以将Wrapper
实例插入字符流:
for (const shared_ptr<Wrapper>& w : wrapped) {
std::cout << *w;
}
是否可以自动转换为大多数派生类型?
正如eeonika的回答所提到的,不,不是。
一般而言,仅给出Wrapper
的声明(或什至是Wrapper
的定义),实现就没有有关从中派生出哪些类的信息。 单独的编译模型(允许源文件被独立编译并链接在一起)意味着编译器不了解在不同编译单元中定义的派生类。
但是,您的主题行实际上是一个错误的问题。 实际上,您已经提供了“ XY问题”的演示,其中您尝试执行“ X”,认为“ Y”将是一个解决方案,因此您已经询问了如何执行“ Y”-在您的情况是没有解决方案的问题,这会阻止人们为您提供帮助。
将来,在提出问题时,请尝试正确地描述您的REAL问题(“ X”),最好不要考虑您要考虑的解决方案的描述(“ Y”)。
幸运的是,很少有人问如何解决“ XY问题”,而实际上却不愿在问题的正文中包含对REAL问题的描述。 这意味着可以提供帮助,我现在将尝试。
如何显示不在父类中的模板化单元成员而无需手动向下转换?
再次如eeroniko所述,实际的解决方案是提供适当的虚拟功能。
就您而言,实际的问题是您的Wrapper
类
class Wrapper { public: virtual void* get1() const = 0; };
提供了一个虚函数,但是当在main()
使用该函数时,该函数没有提供足够的信息来使使用正常工作
cout << *w->get1() << " "; // bummer; can't dereference from void*
要解决此问题,需要执行一些步骤。
第一步,更改Wrapper
以使虚拟函数不会返回void *
。
virtual Wrapper* get1() const = 0;
第二步,声明一个operator<<()
,该operator<<()
允许将Wrapper
输出到流而无需更改该对象。
std::ostream &operator<<(std::ostream &, const Wrapper &);
(可选)可以将此函数声明为Wrapper
的朋友。 const
代表正常的期望,即将对象输出到流不会改变它。 通过引用传递两个参数(每个参数上的&
)很重要。
第三步,提供Wrapper
另一个虚拟功能,可用于执行输出;
virtual void Output(std::ostream &) const;
第四步在模板类Derived
,覆盖两个virtual
函数。
template<typename T> class Derived : public Wrapper
{
T m_value;
public:
Derived(T value) : m_value{ value } {}
Wrapper * get1() const override
{
return this; // note that this does not create a clone
};
virtual void Output(std::ostream &s) const override
{
s << m_value;
};
};
请注意,我已经更改了get1()
因此它返回了当前对象的地址。 您的实现动态创建了一个克隆,这会导致内存泄漏(除非调用者采取特定步骤来释放动态分配的对象)。
第五步,定义先前声明的operator<<()
以便调用虚函数
std::ostream &operator<<(std::ostream &s, const Wrapper &w)
{
w.Output(s);
return s;
}
w.Output(s)
实际上是对虚函数的调用。 因此,如果w
是对Derived<int>
的引用,则w.Output(s)
将正确调用Derived<int>::Output()
而无需以某种方式将Wrapper
为Derived<int>
。
将所有内容放在一起,您会发现该语句
cout << *w->get1() << " ";
现在有效。 机制是get1()
返回对象的地址(即,它返回w
)。 *
获得对指向对象的引用。 然后, operator<<()
调用虚拟函数,该函数解析(由于w
指向Derived<int>
或Derived<float>
)到Output()
的正确重载-由于它是模板化的成员, Derived
类,正确访问成员m_value
。
您可能希望考虑的其他优点
1)尽管我已经根据需要省略了模板化Derived<T>
get2()
成员,但是该函数也可以由Derived<T>::Output()
调用(但不能由operator<<()
Wrapper
因为Wrapper
具有没有这样的函数,并且operator<<()
对从Wrapper
派生出哪些类一无所知。
2)虽然你的陈述
cout << *w->get1() << " ";
现在可以使用了,可以完全省略与get1()
有关的所有内容,而只需
cout << *w << " ";
正确输出对象,因为*w
获得对w
指向的对象的引用( Wrapper &
类型)。 这意味着operator<<()
将正确调用虚拟函数Output()
的最派生形式。
最后一点,我再次强调,无需解决您提出的不可能的问题(如何自动转换为派生最多的类型)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.