【问题标题】:How to make an vector of abstract template class如何制作抽象模板类的向量
【发布时间】:2013-07-09 09:49:57
【问题描述】:

以下不起作用:

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

现在如何制作不同规则的向量?我知道,我必须使用指针......但是我还需要做什么才能让它工作?我怎样才能改变我的基础结构来完成这项工作?

我使用如下接口:

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

我的示例类如下所示:

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

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

【问题讨论】:

  • 为什么不起作用?如果RuleRangeDouble 公开派生自IRule,那么它没有理由不工作。
  • @JamesKanze 归结为 IRule 是一个类模板。
  • @juanchopanza 我错过了。如果IRule 是模板,则IRule* 不是有效类型。

标签: c++ templates vector virtual abstract


【解决方案1】:

向量必须是实际类型的向量,例如std::vector&lt;IRule&lt;double&gt;*&gt;Irule 本身不是一个类型,它是一个类模板。所以你需要

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

如果模板参数不是接口的一部分,可以引入一个通用的基类。别忘了给它一个virtual析构函数:

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

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

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

那么您的原始用例示例将起作用:

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

【讨论】:

  • 这是我的问题,我想要一个未定义类型的向量,只是指针...我想为我的向量中的所有对象调用 isValid...无论它们是什么类型...因为除了 RuleRangeDouble 类之外,还有更多的类。我不知道,也许存在一些包装解决方案?我只是想不出一个……而且我找不到任何人工作……
  • @prom85 那么你需要一个通用的基类,或者一个变体类型,例如boost::anyboost::variant
  • 实际上,我不知道你是什么意思,基类必须在IRule 接口之上,因此也需要是模板......我不能不幸的是,使用 boost...
  • @prom85 不,关键是公共基类,它不能是模板。类模板不是类型,它是创建类型的模板。向量需要一个实际的类型。
  • 所以这意味着,不可能在 c++ 中实现我想要的?一个包含所有实现相同接口的对象的向量,以便我可以通过向量运行并调用接口函数?不是,如果接口是模板?
【解决方案2】:

您可以通过多种方式实现 getBestValidValue() 之类的东西。一种是定义一个特殊的通用(但非模板)返回类型,用于getBestValidValue(),也作为isValid(value) 的参数类型(juanchopanza 的答案在这里有问题):

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
  }
  ....
}

【讨论】:

  • 非常感谢,我得到了这个解决方案,它可以满足我的需求。我只是不能使用std::enable_if,因为我必须使用 Visual Studio 2005 并且该库不是其中的一部分...
  • @prom85 您可以轻松定义自己的enable_iftemplate&lt;bool=true, typename T=void&gt; struct enable_if { typedef T type; };template&lt;typename&gt; struct enable_if&lt;false,T&gt; {};
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-04
  • 1970-01-01
  • 2010-12-02
  • 2019-12-27
  • 1970-01-01
  • 2015-11-18
相关资源
最近更新 更多