如何在STL容器中使用策略模式?

时间:2014-04-11 08:38:34

标签: c++ templates polymorphism containers covariance

假设我有一个名为BinaryClassifier的策略界面,可以使用Sample并返回double,表示属于正类的Sample对象的概率:

struct BinaryClassifier {
  virtual ~BinaryClassifier(){}
  virtual double classify(std::shared_ptr<Sample> sample) const = 0;
};

我们可能有几个BinaryClassifier的实现,例如LogisticRegressionBinaryClassifier

Sample又是一个只暴露两种方法的界面:

struct Sample {
  virtual ~Sample() {}
  InputFeatures const& get_input_features() const = 0;
  double get_label() const = 0;
};

除了这两种方法之外,Sample的具体实现暴露了完全不同的接口(即,它们是不相关的),它们共同的唯一方面是它们可以通过二元分类器进行分类。

到目前为止一切顺利。

当我们决定引入BinaryClassifier::train方法时出现问题:

struct BinaryClassifier {
  virtual ~BinaryClassifier(){}
  virtual double classify(std::shared_ptr<Sample> sample) const = 0;
  virtual void train(std::vector<std::shared_ptr<Sample>> samples) = 0;
};

此时,以下操作无效:

std::vector<std::shared_ptr<ConcreteSample>> concreteSamples = ...;
concreteBinaryClassifier.train(concreteSamples);

那是因为std::vector<std::shared_ptr<ConcreteSample>>std::vector<std::shared_ptr<Sample>>是两种不相关的类型。

C ++ - ish解决方案将依赖于模板:

      template<class SampleType>
      virtual void train(std::vector<std::shared_ptr<SampleType>> samples) = 0; // non-working code, template method cannot be virtual

但模板方法不能是virtual。尽管如此,我希望BinaryClassifier成为一个战略界面,因为可能存在许多可能的BinaryClassifier实现。在这一点上,即使设计看起来很合理,我也陷入了死胡同。

编辑:此外,对于BinaryClassifier向量,可能会使用ConcreteSampleA向量进行训练,同时对ConcreteSampleB类型的对象进行分类< / p>

哪种方法是用大多数C ++建模这种情况的正确方法?

2 个答案:

答案 0 :(得分:0)

您可以将BinaryClassifier设为模板类

template<SampleType> class BinaryClassifier
{
    virtual void train(std::vector<std::shared_ptr<SampleType>> samples) = 0;
}

答案 1 :(得分:0)

您无法在BinaryClassifier上训练ConcreteSampleA,然后使用它对任意随机ConcreteSampleB进行分类。因此,样本类型是BinaryClassifier的固有部分。 Nullref的答案是合理的:使样本类型成为模板参数。

正如您所发现的,这意味着不再需要Sample接口。好。 std::vector<int>不要求int也来自某个Element接口。

当你摆脱东西时,InputFeatures看起来也很可疑。我只是说get_input_features必须返回一些std::tuple,其成员类型都已定义std::less。由于它不再是虚拟的,因此您不必关心不同的样本类型返回不同的元组。我绝对不会硬编码get_label必须返回double。无论如何,这是一种奇怪的标签类型。

现在你说用样本类型A进行训练然后对样本类型B进行分类可能是有意义的。这就是你进行改进的地方:似乎实际的兼容性要求是它们返回相同的元组。因此,Nullref的更好的解决方案是对BinaryClassifier返回的元组类型的get_input_sample进行模板化。

[编辑] 此外,classify不需要共同拥有。通过Sample const&train()真的应该只使用迭代器对。 C ++约定是将一组对象作为范围传递。