Generic base class with multiple template specialized derived classes

前端 未结 4 1003
梦如初夏
梦如初夏 2021-02-09 02:58

I have a finite amount of classes with the nearly-same implementation, the only different being the underlying type of data they manipulate:

class IntContainer
{         


        
相关标签:
4条回答
  • 2021-02-09 03:23

    This is a solution for any types of classes that can round-trip through a stringstream, and such conversion is the right way to convert between types. It isn't efficient at all:

    struct BaseContainer {
    protected:
      boost::any data;
      std::function< std::string( boost::any const& ) > toString;
      virtual void setDataAny( boost::any x, std::function< std::string( boost::any const& ) > convert ) {
        data = x;
        toString = convert;
      }
    public:
      virtual boost::any getDataAny() const {
        return data;
      }
      template<typename T>
      void setData( T const& t ) {
        setDataAny( boost::any(t), []( boost::any const& a )->std::string {
          std::string retval;
          std::stringstream ss;
          try
          {
            ss << boost::any_cast< T >(a);
            ss >> retval;
            return retval;
          } catch(const boost::bad_any_cast &) {
            return retval;
          }
        });
      };
    template<typename T>
    struct TypedContainer:BaseContainer {
    public:
      T getData() const {
        T retval;
        try {
          retval = boost::any_cast<T>(getDataAny());
          return retval;
        } catch(const boost::bad_any_cast &) {
          std::string str = toString( getDataAny() );
          std::stringstream ss;
          ss << str;
          ss >> retval;
          return retval;
        }
      }
    };
    

    with fewer types, you could do something similar, so long as you have conversion functions between them.

    Alternatively, if you like exceptions, you could throw.

    Alternatively, you could use boost::variants, which do no conversions, but work from a finite list of types (they are basically tagged unions that support more types than C++03 lets union do, and with some nice semantics on assign/copy/etc).

    0 讨论(0)
  • 2021-02-09 03:29

    There is simply no way to do what you want.

    The problem is, if this was allowed, the compiler would have to generate as many virtual methods in the base class as there are possible specializations of the template child class (ie. an infinity) which is not possible.

    0 讨论(0)
  • 2021-02-09 03:32

    How about making base template too? Of course there is no way you can do something like

    std::vector<Base> v;
    v.push_back(intf);
    v.push_back(boolf);
    

    but the rest you can achieve with something simple as

    template<typename T>
    class Base
    {
    public:
        virtual void setData(T data) = 0;
        virtual T getData() = 0;
    };
    
    // Modify GenericContainer's definition like so
    template<typename T>
    class GenericContainer : Base<T> { 
        T d;
        public:
        virtual void setData(T data) {d = data;}
        virtual T getData() { return d; }
    };
    

    You can use it in any way as long as types match.

    IntContainer intc = IntContainer();
    intc.setData(42);
    std::cout << intc.getData() << std::endl;
    
    BoolContainer boolc = BoolContainer();
    boolc.setData(true);
    std::cout << boolc.getData() << std::endl;
    
    std::vector<IntContainer> v;
    v.push_back(intc);
    // v.push_back(boolc); No can't do.
    
    0 讨论(0)
  • 2021-02-09 03:35

    Assuming you have some design flexibility, you can change your interface to accommodate this, although its not as efficient as an infinite virtual table

    You can set values through construction, or >>

    You can get values through <<

    Your vector needs to be a base pointer or reference, the size of each base object is variable, the pointer, explicit or implicit through a reference is of fixed size

    Notice that copies are more efficient if the compiler knows that it is copying from one generic to another as opposed to base to base

    #include <iostream>
    #include <sstream>
    #include <vector>
    
    class gen_base
    {
    public:
        virtual std::ostream & output(std::ostream& S) const = 0;
        virtual std::istream & input(std::istream& S) = 0;
    
        friend std::istream & operator >> (std::istream &S, gen_base &g) {
            return g.input(S);
        }
    
        friend std::ostream & operator << (std::ostream &S, const gen_base &g) {
            return g.output(S);
        }
    };
    
    template<typename T>
    class GenericContainer : public gen_base
    {
    public:
        GenericContainer(T data) : _data(data) {}
        GenericContainer(const gen_base& other) {
    // std::cout << "EXPENSIVE" << std::endl;
            std::stringstream cvt;
            other.output(cvt);
            input(cvt);
        }
        template <class U>
        GenericContainer(const GenericContainer<U>& other)
        {
    // std::cout << "CHEAP" << std::endl;
            _data=other.getData();
        }
        virtual std::istream & input(std::istream &S) {
            return (S >> _data);
        }
        virtual std::ostream & output(std::ostream &S) const {
            return (S << _data);
        }
        T getData() const {
          return _data;
        }
    private:
        T _data;
    };
    
    typedef GenericContainer<int> IntContainer;
    typedef GenericContainer<bool> BoolContainer;
    typedef GenericContainer<std::string> StringContainer;
    
    int main(int argc, char const *argv[])
    {
        IntContainer * intc = new IntContainer(42);
        std::cout << *intc << std::endl;
    
        gen_base * boolc = new BoolContainer(*intc);
        std::cout << *boolc << std::endl;
    
        IntContainer * intc2 = new IntContainer(*boolc);
        std::cout << *intc2 << std::endl;
    
        std::vector<gen_base *> v; // has to be pointer to base;
        v.push_back(intc);
        v.push_back(boolc);
        v.push_back(intc2);
    
        for (std::vector<gen_base *>::iterator it = v.begin() ; it != v.end(); ++it)
            std::cout << **it << std::endl;
    
        delete intc;
        delete boolc;
    
        return 0;
    }
    
    0 讨论(0)
提交回复
热议问题