C++ 将未知类型传递给虚函数
C++ passing unknown type to a virtual function
我正在用 C++ 编写,我想将未知类型(仅在 运行 时间内已知)传递给纯虚函数:
virtual void DoSomething(??? data);
其中 DoSomething
是派生 class 中纯虚函数的实现。
我打算使用模板,但事实证明虚函数和模板不能一起工作:Can a C++ class member function template be virtual?
我想避免对传递给函数的所有 class 使用基数 class(类似于 C# 中的 object)。
提前致谢
您需要 type erasure. An example of this is the general purpose boost::any
(以及 C++17 中的 std::any
)。
virtual void DoSomething(boost::any const& data);
然后每个子 class 可以尝试 safe any_cast
以获得它期望的数据。
void DoSomething(boost::any const& data) {
auto p = any_cast<std::string>(&data);
if(p) {
// do something with the string pointer we extracted
}
}
如果您寻求的行为范围受到更多限制,您当然可以推出自己的类型擦除抽象。
类似的东西?:
class Foo
{
virtual ~Foo() = 0;
};
template <typename T>
class Bar : public Foo
{
T object;
}
...
virtual void DoSomething(Foo* data)
{
Bar<int>* temp = dynamic_cast<Bar<int>*>(data);
if (temp)
std::count<<temp->object;
}
如果您不想使用 boost/C++17 any,请考虑从基数 class 派生 'doSometing' 函数的参数,并向右动态转换 class对象。在这种情况下,您可以在运行时检查您是否获得了一个有效的指针。
class param{
public:
virtual ~param(){};
};
template <typename T>
struct specificParam:param{
specificParam(T p):param(p){}
T param;
};
class Foo
{
public:
virtual void doSomething(param* data) = 0;
};
template <typename T>
class Bar : public Foo
{
public:
virtual void doSomething(param* data){
specificParam<T> *p = dynamic_cast<specificParam<T> *>(data);
if (p != nullptr){
std::cout<<"Bar got:" << p->param << "\n";
}
else {
std::cout<<"Bar: parameter type error.\n";
}
}
};
int main(){
Bar<char> obj1;
Bar<int> obj2;
Bar<float> obj3;
specificParam<char> t1('a');
specificParam<int> t2(1);
specificParam<float> t3(2.2);
obj1.doSomething(&t1); //Bar got:a
obj2.doSomething(&t2); //Bar got:1
obj3.doSomething(&t3); //Bar got:2.2
// trying to access int object with float parameter
obj2.doSomething(&t3); //Bar: parameter type error.
}
最简单(但不安全!)的方法是使用 void* pointer + static cast
class Foo
{
public:
virtual void doSomething(void* data) = 0;
};
template <typename T>
class Bar:public Foo
{
public:
virtual void doSomething(void* data){
T* pData = static_cast<T*>(data);
std::cout<<"Bar1 got:" << *pData << "\n";
}
};
int main(){
Bar<char> obj1;
Bar<int> obj2;
Bar<float> obj3;
char c = 'a';
int i = 1;
float f = 2.2;
obj1.doSomething(&c); // Bar1 got:a
obj2.doSomething(&i); // Bar1 got:1
obj3.doSomething(&f); // Bar1 got:2.2
//obj2.doSomething(&c); // Very bad!!!
}
类型擦除不是唯一的可能性。
您可能有兴趣使用访问者模式:将 std::variant 作为参数并使用包含您要实现的模板代码的 lambda 来访问它:
virtual void doSomething(std::variant<int,float/*,...*/> data)
{
visit([=](auto v){/*...*/;},data);
}
我正在用 C++ 编写,我想将未知类型(仅在 运行 时间内已知)传递给纯虚函数:
virtual void DoSomething(??? data);
其中 DoSomething
是派生 class 中纯虚函数的实现。
我打算使用模板,但事实证明虚函数和模板不能一起工作:Can a C++ class member function template be virtual?
我想避免对传递给函数的所有 class 使用基数 class(类似于 C# 中的 object)。
提前致谢
您需要 type erasure. An example of this is the general purpose boost::any
(以及 C++17 中的 std::any
)。
virtual void DoSomething(boost::any const& data);
然后每个子 class 可以尝试 safe any_cast
以获得它期望的数据。
void DoSomething(boost::any const& data) {
auto p = any_cast<std::string>(&data);
if(p) {
// do something with the string pointer we extracted
}
}
如果您寻求的行为范围受到更多限制,您当然可以推出自己的类型擦除抽象。
类似的东西?:
class Foo
{
virtual ~Foo() = 0;
};
template <typename T>
class Bar : public Foo
{
T object;
}
...
virtual void DoSomething(Foo* data)
{
Bar<int>* temp = dynamic_cast<Bar<int>*>(data);
if (temp)
std::count<<temp->object;
}
如果您不想使用 boost/C++17 any,请考虑从基数 class 派生 'doSometing' 函数的参数,并向右动态转换 class对象。在这种情况下,您可以在运行时检查您是否获得了一个有效的指针。
class param{
public:
virtual ~param(){};
};
template <typename T>
struct specificParam:param{
specificParam(T p):param(p){}
T param;
};
class Foo
{
public:
virtual void doSomething(param* data) = 0;
};
template <typename T>
class Bar : public Foo
{
public:
virtual void doSomething(param* data){
specificParam<T> *p = dynamic_cast<specificParam<T> *>(data);
if (p != nullptr){
std::cout<<"Bar got:" << p->param << "\n";
}
else {
std::cout<<"Bar: parameter type error.\n";
}
}
};
int main(){
Bar<char> obj1;
Bar<int> obj2;
Bar<float> obj3;
specificParam<char> t1('a');
specificParam<int> t2(1);
specificParam<float> t3(2.2);
obj1.doSomething(&t1); //Bar got:a
obj2.doSomething(&t2); //Bar got:1
obj3.doSomething(&t3); //Bar got:2.2
// trying to access int object with float parameter
obj2.doSomething(&t3); //Bar: parameter type error.
}
最简单(但不安全!)的方法是使用 void* pointer + static cast
class Foo
{
public:
virtual void doSomething(void* data) = 0;
};
template <typename T>
class Bar:public Foo
{
public:
virtual void doSomething(void* data){
T* pData = static_cast<T*>(data);
std::cout<<"Bar1 got:" << *pData << "\n";
}
};
int main(){
Bar<char> obj1;
Bar<int> obj2;
Bar<float> obj3;
char c = 'a';
int i = 1;
float f = 2.2;
obj1.doSomething(&c); // Bar1 got:a
obj2.doSomething(&i); // Bar1 got:1
obj3.doSomething(&f); // Bar1 got:2.2
//obj2.doSomething(&c); // Very bad!!!
}
类型擦除不是唯一的可能性。
您可能有兴趣使用访问者模式:将 std::variant 作为参数并使用包含您要实现的模板代码的 lambda 来访问它:
virtual void doSomething(std::variant<int,float/*,...*/> data)
{
visit([=](auto v){/*...*/;},data);
}