简体   繁体   中英

How to check the type of a template parameter for a polymorphic subclass

I have a class hierarchy in which I<T> and O<T> are a templated classes that extend B (regular class). I aggregates O by means of a pointer, like so:

class B { ... };
template <class T>
class O : public B
{
    T getValue() { return value; }
private:
    T value;
}
template <class T>
class I : public B
{
    T getValue() { return ptr->getValue(); }
    void setPtr(O<T>* po) { ptr = po; }
private:
    O<T>* ptr;
}

The code above is indeed simplified to show only its bare bones. "Connecting" I and O is done automatically although through user configuration parameters. Because of that, a Singleton table stores polymorphic B pointers to all I and O objects and performs the linking by calling I::setPtr as necessary. The problem though is that the user can actually mess up and confuse template types of I and O objects, eg user can ask to connect an I<int> with an O<float> . Eg:

O<int>* oi = new O<int>();
O<float>* of = new O<float>(); 
I<int> ii;
ii.setPtr(oi);  /* This is OK. */
O<int>* oi_wrong = static_cast<O<int>*>((B*)of);  /* This happens. */
ii.setPtr(oi_wrong); /* This is bad. */

In order to prevent this sort of behaviour, I included a member variable in both I and O that stores typeid(T).name() , thus allowing to check whether the template parameter is actually the same.

Is there a better way?

EDIT: Would there be a better way to do the casting here? Is it not a good idea to store and work with polymorphic pointers in this case?

There's really nothing much you can do if your user is going out of the way to misuse your interface - you should (reasonably) design your code without assuming that your user is going to use static_cast and reinterpret_cast liberally.

In your case, I wouldn't add any additional code/checks. Just assume that your user acts in a reasonable manner.


If you really want, you could use assert and dynamic_cast to check if the passed pointer is actually what you expected in DEBUG builds:

struct A { virtual ~A() { } };
struct B : A { };
struct C : A { };

void setPtr(A* po) 
{ 
    assert(dynamic_cast<C*>(po) != nullptr);
}

int main()
{
    B b;
    setPtr((C*)&b); // might trigger assertion
}

But I strongly suggest against it, as this is overkill.

How about using a friend function for setPtr(), so the user is forced to input fitting types for I and O:

    class B { };
    template <typename T>
    class O : public B
    {
    public:
      T getValue() { return value; }
      void setPtr(I<T>& ref_i) { ref_i.ptr = this; };
    private:
      T value;
    };

    template <typename T>
    class I : public B
    {
    public:
      T getValue() { return ptr->getValue(); }
      friend void O::setPtr(I<T>& ref_i)
    private:
      O<T>* ptr;
    };


    int main()
    {
      O<int>* oi = new O<int>();
      O<float>* of = new O<float>();
      I<int> ii;
      of->setPtr(ii);  /* This fails. */
      O<int>* oi_wrong = static_cast<O<int>*>((B*)of);  /* This CAN                 happen. */
      oi->setPtr(ii); /* This is OK. */
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM