prom85
prom85

Reputation: 17868

How to make an vector of abstract template class

Following does not work:

std::vector<IRule*> vec;
RuleRangeDouble *rule = new RuleRangeDouble(0, 100);
vec.push_back(rule);

Now how can a make a vector of different rules? I know, I have to use pointers... But what else do I have to do to get this working? How can I change my base structure to get this work?

I use an Interface like the following:

// Interface
    template <typename T>
    class IRule
    {
        public:
            virtual bool isValid(T value) = 0;
    };

And my example class looks like this:

class RuleRangeDouble : public IRule<double>
    {
        private:
            double min;
            double max;
        public:

            bool isValid(double value)
            {
....
            };
    };

Upvotes: 1

Views: 913

Answers (2)

Walter
Walter

Reputation: 45444

You can implement something like getBestValidValue()in several ways. One is to define a special general (but non-template) return type to be used for getBestValidValue() and also as argument type for isValid(value) (juanchopanza's answer was faulty here):

class ValueType
{
  enum { is_int, is_double } my_type;  // add more if you want
  union { my_int; my_double; };        // union only works for simple types
public:
  // implicit construction from allowed types
  ValueType(int x) : my_type(is_int), my_int(x) {}
  ValueType(double x) : my_type(is_double), my_double(x) {}
  // use SFINAE for is<type>() function
  template<typename T>
  typename std::enable_if<std::is_same<T,int>::value,bool>::type
  is() const { return my_type==is_int; }
  template<typename T>
  typename std::enable_if<std::is_same<T,double>::value,bool>::type
  is() const { return my_type==is_double; }
  // implicit type conversion to allowed types
  // note: does not assert(is<T>())
  operator int() { return my_int; }
  operator double() { return my_double; }
};

class IRule
{
public:
  virtual bool isValid(ValueType const&) const = 0; // fixed bug in juanchopanza's answer
  virtual ValueType getBestValidValue() const = 0;  // return any type of value
  virtual ~IRule() {}
};

template<typename T>
class Rule : public IRule
{
protected:
  virtual bool valid(T) const = 0; 
  virtual T bestValidValue() const = 0;
public:
  bool isValid(ValueType const&value) const
  {
    return value.is<T>()
        && valid(value);         // implicit type conversion to T
  }
  ValueType getBestValidValue() const
  {
    return bestValidValue();     // implicit construction of ValueType
  }
  ....
}

Upvotes: 2

juanchopanza
juanchopanza

Reputation: 227478

The vector needs to be a vector of an actual type, for example std::vector<IRule<double>*>. Irule on its own is not a type, it is a class template. So you would need

std::vector<IRule<double>*> vec;
RuleRangeDouble *rule = new RuleRangeDouble(0, 100);
vec.push_back(rule);

If the template parameter is not part of the interface, you can introduce a common base class. Don't forget to give it a virtual destructor:

class IRule
{
    public:
        virtual bool isValid(T value) = 0;
        virtual ~IRule() {}
};

template <typename T>
class Rule : public IRule 
{
  .....
};

class RuleRangeDouble : public Rule<double>
{
  ....
};

Then your original use case sample would work:

std::vector<IRule*> vec; // IRule really is a type now
RuleRangeDouble *rule = new RuleRangeDouble(0, 100);
vec.push_back(rule);

Upvotes: 3

Related Questions