Предположим, что у меня есть интерфейс стратегии с именем 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 ++ будет использование шаблонов:
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
Как правильно моделировать эту ситуацию наиболее подходящим для C ++ способом?
Вы можете сделать свой BinaryClassifier классом шаблона
template<SampleType> class BinaryClassifier
{
virtual void train(std::vector<std::shared_ptr<SampleType>> samples) = 0;
}
Вы не можете тренировать BinaryClassifier
на ConcreteSampleA
а затем использовать его для классификации любого случайного ConcreteSampleB
, Поэтому тип выборки является неотъемлемой частью BinaryClassifier
, Ответ Нуллрефа вполне обоснован: сделайте тип образца параметром шаблона.
Как вы обнаружили, это означает, что больше нет необходимости Sample
интерфейс. Хорошо. std::vector<int>
не требует этого int
происходит от некоторых Element
Интерфейс тоже.
Пока ты избавляешься от вещей, InputFeatures
выглядит подозрительным тоже. Я бы просто сказал, что get_input_features
должен вернуть немного std::tuple
чьи типы членов все имеют std::less
определены. Поскольку он больше не является виртуальным, вам все равно, что разные типы семплов возвращают разные кортежи. И я определенно не стал бы это жестко кодировать get_label
должен вернуть double
, В любом случае, это странный тип ярлыка.
Теперь вы сказали, что, возможно, имеет смысл потренироваться с типом выборки A, а затем классифицировать тип выборки B. Вот где вы получите уточнение: кажется, что реальное требование совместимости состоит в том, что они возвращают один и тот же кортеж. Следовательно, лучшее решение, которое Nullref’s, — это шаблонизировать BinaryClassifier
на тип кортежа, возвращаемый get_input_sample
,
classify
не нужно принимать совладение. Пройти Sample const&
, train()
на самом деле следует взять только пару итераторов. Соглашение C ++ — передавать группу объектов как диапазон.