如果我们有一个基*类,如何访问派生模板类的成员函数



我有一个从基类派生的模板类。

同样在main中,我有一个vector容器,其中包含各种类型的派生类。不知道Derived类的类型,我怎么能 在 Main 中访问其成员函数?

模板专用化可能是解决此问题的好方法。但是,我该怎么做呢?

class Base{ // The base class of a template class
public:
Base(){}
virtual ~Base(){}
};
template <typename T>
class Derived : public Base{ // The template class,
// derived from the base class.
T data;
public:
Derived(){}
~Derived(){}
T  get()       { return data; }
void set( T val) { data = val;  }
};
int main() {
vector <Base*> myObject;
// I have different types of Derived class
// in a vector conteiner.
Derived<int>  *D1=new Derived<int>;
Derived<float>*D2=new Derived<float>;
Derived<char> *D3=new Derived<char>;
myObject.push_back(D1);
myObject.push_back(D2);
myObject.push_back(D3);
for (int i=0;i<myObject.size();i++){
// myObject[i]->set(4);  Error! : myObject is a vector of <Base*>
// myObject[i]->get();   Error! : not a vector of <Derived*>
}
return 0;
}

您将无法使用非模板基类执行所需的操作。 您尝试做的是通过存储指向非模板基类的指针来擦除模板派生类的类型。 这称为类型擦除 - https://en.wikipedia.org/wiki/Type_erasure

可以dynamic_cast,然后打开派生类的类型。 例如

for (int i=0;i<myObject.size();i++){
if (auto* ptr = dynamic_cast<Derived<int>*>(myObject[i])) {
ptr->set(4);
ptr->get();
} else if (auto* ptr = dynamic_cast<Derived<double>*>(myObject[i])) {
ptr->set(1.0);
ptr->get();
}
}

您可能会对std::any如何解决此接口问题感兴趣。 看看这里 http://en.cppreference.com/w/cpp/utility/any/any_cast

另一种方法是不投资类型擦除并使用std::variant

您想要的确切行为无法通过 c++ 的标准方法实现。此设计不是多态的,并且对象中没有任何类型信息。此外,您的对象似乎也没有通用接口(在 c++ 上下文中(,因为所有函数都以不同的类型运行。

这似乎是 Variant 类型的实现,您希望在一个容器中存储不同的值(整数、字符串、浮点数等(。

解决此问题的最简单方法是将type文件添加到基类,并为派生类中的每个类型为其分配特定值。但是,您应该手动检查它,并在每次要访问实际值时转换为所需的实际时间。

enum ValueType
{
Bool, Int, Float
};
class Base
{
protected:
ValueType _type;
public:
ValueType type() const { return _type; }
};
// usage example
Base *val = vector[something];
switch (val->type()) {
case Int: { int intvalue = Derived<int>(val)->get(); }
}

另一种方法是通过让它们返回/接受一些可以表示任何类型的值的通用类型(如字符串(来为get/set创建通用接口,并像往常一样在没有模板的情况下使用继承和虚函数。此实现的缺点是字符串处理和存储的成本。

class Base
{
public:
virtual string get() const = 0;
virtual void set(const string &newval) = 0;
};
class IntValue : public Base
{
public:
/* imagine we have that magic IntToStr function */
string get() const override { return IntToStr(_value); }
private:
int _value;
};

我相信最有效的方法是将所有可能的 getter 和 setter 放在基类中,并将它们覆盖到特定的派生类中。有了这个,您可以获得统一的界面,并且可以进行额外的方便的值转换(例如将 int 读取为 char 或 bool 或字符串(。

class Base
{
public:
virtual bool getBool() const = 0;
virtual int getInt() const = 0;
// etc for other types getters and setters
};
class IntValue
{
public:
bool getBool() const override { return _value != 0; }
int getInt() const override { return _value; }
// for incompatible types - throw error or return default value
};

还有另一种方法可以使其尽可能快,但也尽可能丑陋,那就是让模板化的 get/set 版本及其实现(没有虚拟(专门针对每种类型和每种可能的变体类型。有了这个,您必须保留内部type变量,该变量将保存实际值类型。在这种情况下,您根本不需要继承。

问题在于实现类型的方式。当您声明std::vector<Base*>时,C++准备存储能够Base功能的对象,仅此而已。当您将这三个对象放入矢量中时,C++会忘记它们的基础类型。所以当你调用myObject[i]->set(4)时,系统不可能知道该呼叫哪个set

通常,解决方案是在基类中创建虚拟方法。但是,正如您提到的,您无法这样做,因为您在getset中具有模板类型。下一个想法是实现一个模板化的基类并Derived<T>子类Base<T>。这将解决问题,但它会创建一个新问题:你不能有一个异构对象的向量。

所以我认为从根本上说,你需要考虑你想做什么。这里有一些问题要问自己。

您是否预先知道要存储
  1. 多少对象以及将存储哪些对象?如果在编译时知道对象的数量和类型,请考虑Derived对象的std::tuple

  2. 既然您将所有对象都设置为整数4,您是否知道所有对象类型都可以转换为整数/从整数转换?如果是这样,您可以将get_intset_int编写为基类中的虚函数,并调用它们可以委托给子类中的getset

  3. 如果您不知道预先需要多少对象,但您知道它们都来自一组有限的对象,请考虑std::vectorstd::variant(或boost::variant,如果您无法访问 C++17(。

  4. 如果你真的需要所有的通用性,你可以使用std::any来规避类型系统(或者,如果你没有C++17,也可以再次boost::any(。但要认真考虑一下你是否能做得更好。上述解决方案将为您提供更好的类型安全性,并使您的代码不易出错。

最新更新