假设我有以下两个类Super
和Deriv
,其中Deriv
是Super
的子类。
Super
和Deriv
都是模板化类。我想创建vector
类型Super<?>
。 ?
表示任何类型的地方。目前我已经想出了这个:
#include <iostream>
#include <vector>
template <typename T>
class Super {
public:
T val;
Super(T val) : val(val) {};
};
template <typename T>
class Deriv : public Super<T> {
public:
Deriv(T val) : Super<T>(val) {};
};
int main() {
std::vector<Super*> a;
a.push_back(new Deriv<int>(1));
a.push_back(new Deriv<float>(1.0f));
std::cout << a[0]->val << std::endl;
return 0;
}
当然这不起作用,因为std::vector<Super*>
需要像std::vector<Super<int>*>
这样的模板类型。但问题是我只能将项目添加到Super<int>*
类型的向量而不是Super<float>*
。
如何更改此代码以允许我将Super
类型或其衍生物添加到具有int
,float
,short
等任何模板类型的向量中等?
答案 0 :(得分:1)
不确定这是否可以解决您的问题,但它可能会给您一些想法。这里的基本思想是为各种T
创建一个超类。对于基础数据类型,需要包装类。
#include <iostream>
#include <cstdio>
#include <vector>
#include <string>
class SuperT {
public:
virtual std::string AccessData() = 0;
};
class IntWraper : public SuperT {
public:
IntWraper(int i) : val(i) { };
virtual std::string AccessData() { return std::to_string(val); };
private:
int val;
};
class FloatWraper : public SuperT {
public:
FloatWraper(float f) : val(f) { };
virtual std::string AccessData() { return std::to_string(val); };
private:
float val;
};
class RealSuper {
public:
virtual std::string DoSomething() = 0;
};
template <typename T>
class Super : public RealSuper {
public:
T* wraper_val_;
Super(T* w_val) : wraper_val_(w_val) { };
~Super() { if(wraper_val_) delete wraper_val_; };
virtual std::string DoSomething() { return wraper_val_->AccessData(); }
};
template <typename T>
class Deriv : public Super<T> {
public:
Deriv(T* w_val) : Super<T>(w_val) {};
};
int main() {
std::vector<RealSuper*> a;
a.push_back(new Deriv<IntWraper>(new IntWraper(1)));
a.push_back(new Deriv<FloatWraper>(new FloatWraper(1.0f)));
std::cout << a[0]->DoSomething() << std::endl;
std::cout << a[1]->DoSomething() << std::endl;
return 0;
}
答案 1 :(得分:1)
此
std::vector<Super*> a;
错了。
代码中没有名为Super
的类型。是的,你声明了一个名为“超级”的东西,但它不是一个类,它是一个类模板。
该功能的名称说明了一切。这是一个模板。编译器将使用Super
在编译时生成新类型。
例如,Super<int>
在填充T
模板中的漏洞Super
时,请参阅编译器生成的类。
那么为什么a[0]->val
无法工作?好吧,想象一下:
template<>
struct Super<std::string> {
std::string my_val;
};
我们专注于Super
,因此在使用std::string
进行实例化时,它不再拥有val
成员,而是my_val
成员。
现在,您希望这段代码做什么?
std::vector<Super*> a;
a.push_back(new Deriv<std::string>);
std::cout << a[0]->val << std::endl;
不是很令人费解吗?您在运行时需要编译错误。由于变量的存在(或不存在)是在编译时确定的,因此不可能。
现在我们如何解决您的问题?
在您的情况下,它就像添加Super
之上的接口一样简单,并公开实现计算所需行为的函数:
struct Interface {
void print(std::ostream) const = 0;
bool lessThan(double) const = 0;
};
template <typename T>
struct Super : Interface {
T val;
Super(T val_) : val{val_} {};
// We implement the needed behavior.
void print(std::ostream o) const override {
o << val << std::endl;
}
// Example of calculation
bool lessThan(double rhs) const override {
return val < rhs;
}
};
现在你可以做到:
std::vector<Interface*> a;
// ...
a[0]->print(std::cout);
a[0]->lessThan(3.7);
答案 2 :(得分:0)
创建一个新类RealSuper
,缓存该类型是一种可能的解决方法。
这并不完美,但我担心它不会比这更好: -
class RealSuper{ //<-- new class
public: enum TYPE{ type_int, type_float, type_notIni }; // (yes, it is awkward)
TYPE typee = type_notIni;
};
template <typename T> class Super : public RealSuper { //<-- modify
public: T val;
Super(T val) : val(val) {
if( std::is_same<T, int>::value ){
typee = type_int;
}else if( std::is_same<T, float>::value ){
typee = type_float;
}
};
};
template <typename T> class Deriv : public Super<T> {
public: Deriv(T val) : Super<T>(val) {};
};
int main() {
std::vector<RealSuper*> a;
a.push_back(new Deriv<int>(1));
a.push_back(new Deriv<float>(1.0f));
for(auto ele: a){
switch( ele->typee ){
case RealSuper::TYPE::type_int: {
int value=static_cast<Super<int>*>(ele)->val;
std::cout << value << std::endl;
};break;
case RealSuper::TYPE::type_float :{
float value=static_cast<Super<float>*>(ele)->val;
std::cout << value << std::endl;
};break;
}
}
return 0;
}
这是使用虚函数显示方法的另一个demo。
答案 3 :(得分:0)
C ++没有你要求的功能(我相信这是一种具体化)。这意味着您不能在第一个位置存储任意类型,然后在第二个位置的完全不相关的源文件中指定任意操作,然后将操作应用于第三个位置的数据。
您可以解决许多与您要求的问题相近的问题:限制您在第二个位置要执行的操作,或限制您在第一个位置存储的类型,以及问题可以解决。或者,限制在第三个位置支持的x类操作对。
请注意,限制操作集的组合也可以起作用。
理论上,您可以在C ++二进制文件中嵌入完整的编译器或解释器,编译代码并动态加载它。 (这基本上是C#/ Java如何管理具体化)。这对大多数问题来说都是不切实际的。该语言不支持此功能,但这是C ++,您可以做任何事情(甚至编写Java / C#编译器)。
由于没有关于您需要解决的潜在问题的信息,我无法告诉您上述哪种方法是正确的。
这就是为什么“如果我有X我想解决Y,但我无法弄明白X.我知道,我只会问堆栈溢出怎么做X!”被称为X / Y问题。我们可以解决Y,但是你问过哪个X甚至没有描述Y就解决了Y.随意发布Y问题并询问它。使用上面的[问一个问题]按钮。
限制您在存储中处理的类型:
存储std::variant
。使用std::visit
。或者自己编写或使用boost::variant
。
限制要执行的操作:
使用类型擦除在存储类型时生成每种类型的操作。
在通话点限制操作类型对:
使用RTTI来提取存储的类型,使用大型交换机然后使用解决方案。