关于c ++:自动向下转换指向派生对象的指针

Automatic downcast of a pointer to a derived object

早上好,

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

我的问题是:要从指向基类的指针调用派生类的方法,我不能使用虚函数,因为模板函数不能变为虚函数。我需要做一个显式的强制转换,这很繁琐:一旦一个用新的创建了一个数字对象,实际上,需要对数字*进行强制转换,尽管已知该对象是提前编号的。

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

除了繁琐之外,函数只能用于调用"set"方法,并且应该为调用其他方法定义类似的函数。如果我能对我指向的对象进行自动投射,那就简单多了。

这种方法的缺点是:

  • 代码是重复的:如果我定义了另一个函数(如t get()return val;),我将需要另一个myget函数和一组完整的嵌套ifs!
  • 必须通过嵌套if调用显式定义支持类型的列表
  • 代码可能效率低下
  • 编译器知道lst[0](在下面的代码中)指向一个数字对象,尽管lst是从中派生数字对象的元素对象的向量。

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

    如果我有正确的向下转换,那么我可以在number<>类中定义数千个方法,并使用propercasting->function(…)调用它们。

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

    事先谢谢,彼得洛M

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    #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();
    }


    你就快到了。您需要有一个模板化的set方法,该方法使用typeidvoid *指针调用其参数的私有虚拟方法,从而允许重写决定如何处理该方法:

    1
    2
    3
    4
    5
    6
    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的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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所采用的方法。