自动向下转换指向派生对象的指针

时间:2013-07-22 10:05:49

标签: c++ templates downcast

早上好,

我有一个模板化的类,我想通过指针向量来操纵对象。要使用指向templatized类的指针向量,我需要从非模板化的类派生这个类,并且我做到了。

这是我的问题:从指向基类的指针调用派生类的方法,我不能使用虚函数,因为模板函数不能虚拟化。我需要进行一个显式的强制转换,这很乏味:一旦创建了一个带有new的数字对象,实际上,需要对数字*进行向下转换,尽管事先知道该对象是数字。

我以一种尴尬的方式解决了这个问题:函数myset测试了所有受支持的typeid值以获得正确的动态强制转换。它是一系列执行typeid检查的嵌套ifs。

除了单调乏味之外,该函数仅适用于调用'set'方法,并且应该为调用其他方法定义类似的函数。如果我可以对我指向的那种物体进行自动投射,那么它就会变得更加简单。

方法的缺点是:

  1. 代码是重复的:如果我定义了另一个函数(例如T get(){return val;},我需要另一个带有一整套嵌套ifs的myget函数!
  2. 如果调用
  3. ,则必须通过嵌套显式定义支持的类型列表
  4. 代码可能效率低下
  5. 编译器知道lst [0](在下面的代码中)指向一个数字对象,尽管lst是元素对象的向量,其中数字<>对象是派生的。

    是否有一种方法可以自动将定义为base *的指针向下转换为指向实际指向的对象的指针?

    如果我有正确的向下转换,那么我可以在数字<>中定义数千种方法。上课并用适当的广播来称呼他们 - >函数(...)调用

    这是代码(它正常工作,核心是“myset”的定义)

    提前致谢, Pietro M。

    PS我最感兴趣的是标准C ++方法而不使用除STL之外的其他库,例如Boost。

    #include <iostream>
    #include <vector>
    #include <typeinfo>
    
    using namespace std;
    
    class element
    {
    public:
        virtual void print() = 0; // print is not templatized and works properly
        //template <class T> virtual set(T v) = 0; // this would solve all my problems, if only it were legal.
    };
    
    template <class T>
    class number : public element
    {
        T val;
    public:
        void print() {cout << "number is " << val << endl;}
        void set(T v) {val = v;}
    };
    
    // That's the best I can do!
    template <class T>
    void myset(T v, element *ptr)
    {
        // There is a kink in the template: the compiler checks the T type by the value of v, that in this case is an integer:
        // cout << "Type name for the template is: " << typeid(T).name() << endl;
        // cout << "Type name for an integer is:   " << typeid(int).name() << endl;
    
        if (typeid(*ptr) == typeid(number<double>))
        {
            ((number<double> *) ptr) -> set(7);
            return;
        }
        else if (typeid(*ptr) == typeid(number<float>))
        {
            ((number<float> *) ptr) -> set(7);
            return;
        }
        // add other types... (tedious)
        else
        {
            cout << "type not supported" << endl;
        }
    }
    
    int main()
    {
        vector <element *> lst; // list of heterogeneous templatized objects
        lst.push_back(new number<float>);
        lst.push_back(new number<double>);
    
        lst[0] -> print();
    
        //((number<float> *) lst[0]) -> set(7); it's correct but it requires I know the type when I call it (tedious)
    
        myset(7, lst[0]); // may be inefficient, but it works (for the types which are explicitly supported)
    
        // cast_to_what_the_pointer_actually_points_to <lst[0]> -> set(7); // that's what I'd like to do: a downcast function which checks the object type and returns the correct pointer would be able to call any class method...
    
        lst[0] -> print();
    }
    

1 个答案:

答案 0 :(得分:3)

你快到了。您需要一个模板set方法,该方法使用typeidvoid *指向其参数的指针调用私有虚方法,允许覆盖决定如何处理它:

class element
{
    virtual void set_impl(const std::type_info &, const void *) = 0;
public:
    template <class T> void set(T v) { set_impl(typeid(v), &v); }
};

以及如何编写set_impl

的示例
template <class T>
class number : public element
{
    T val;
    void set_impl(const std::type_info &ti, const void *pv) {
        if (ti == typeid(T)) {
            val = *static_cast<const T *>(pv);
        } else {
            throw std::invalid_argument("incorrect type to set()");
        }
    }
};

这类似于Boost.Any采取的方法。