Boost: хранить указатели на распределения в векторе

Привет, эксперты по обмену стеками,

Я пытаюсь собрать указатели на различные статистические распределения, предоставляемые Boost в одном векторе.
Если бы распределения были получены из одного (виртуального) родительского класса, я мог бы написать что-то вроде

std::vector<Parent> v;

boost::math::normal_distribution<double> n;
boost::math::students_t_distribution<float> t(4);

boost::math::normal_distribution<double> *p1 = new boost::math::normal_distribution<double>(n);
boost::math::students_t_distribution<float> *p2 = new boost::math::students_t_distribution<float>(t);
v.push_back(p1);
v.push_back(p2);

а затем перебрать вектор и применить функции и т. д. к указателям с разыменованными ссылками.
Но так как это не тот случай, я не знаю, как хранить указатели в одном месте?

Поэтому мой вопрос: есть ли способ хранить указатели на разные классы шаблонов в одной переменной / списке / векторе … (это может быть удобно обработано, например, как std :: vector).

Обратите внимание, что, например, функция плотности pst Boost может применяться к указателям с разыменованными ссылками независимо от конкретного типа (поэтому в некоторых случаях их сохранение в одном векторе имеет смысл).

////////////////////////////////////////////////// ////////////////////////

Я поиграл с разными (хорошими) ответами и, наконец, решил придерживаться boost :: варианте в сочетании с boost :: static_visitor.
Ниже приведено полное приложение, которое делает то, что я изложил в моем первоначальном вопросе:

#include <boost/math/distributions.hpp>
#include <boost/variant.hpp>
#include <vector>
#include <iostream>

//template based visitor to invoke the cdf function on the distribution
class cdf_visitor_generic : public boost::static_visitor<double>
{
public:

//constructor to handle input arguments
cdf_visitor_generic(const double &x) : _x(x) {}

template <typename T>
double operator()(T &operand) const {
return(boost::math::cdf(operand,_x));
}

private:
double _x;
};

//shorten typing
typedef boost::variant< boost::math::normal_distribution<double>, boost::math::students_t_distribution<double> > Distribution;

int main (int, char*[])
{
//example distributions
boost::math::normal_distribution<double> s;
boost::math::students_t_distribution<double> t(1);

//build a variant
Distribution v = t;

//example value for evaluation
double x = 1.96;

//evaluation at one point
double y = boost::apply_visitor( cdf_visitor_generic(x),v);
std::cout << y << std::endl;//build a vector and apply to all elements of it:
std::vector<Distribution> vec_v;

vec_v.push_back(s);
vec_v.push_back(t);for (std::vector<Distribution>::const_iterator iter = vec_v.begin(); iter != vec_v.end(); ++iter){

//apply cdf to dereferenced iterator
double test = boost::apply_visitor( cdf_visitor_generic(x), *iter);
std::cout << test << std::endl;
}
return 0;
}

Единственный недостаток, который я вижу, заключается в том, что тип дистрибутива должен быть явно указан (в варианте), поэтому boost :: any добавляет больше свободы.

Спасибо за большую помощь!

моток

3

Решение

Вы можете использовать variant:

std::vector<boost::variant<
boost::math::normal_distribution<double>,
boost::math::students_t_distribution<float>
> > v;

boost::math::normal_distribution<double> n;
boost::math::students_t_distribution<float> t(4);

v.push_back(n);
v.push_back(t);

У меня есть несколько ответов, которые показывают, как использовать эти элементы «полиморфно» (хотя полиморфизм заключается в статическом переключении типов вместо переключения vtable). Я добавлю ссылку или две в ближайшее время.

Некоторые из связанных ответов показывают «ручной» подход к стиранию типов

PS. Я должен вероятно упомянуть boost::any тоже, но мне это не нравится по нескольким причинам. Я не рекомендую это для этой цели.

3

Другие решения

Вы не можете хранить указатели на несвязанные типы в одном векторе. Один из способов добиться этого — сделать вектор пустым *:

std::vector<void*>

Но я бы настоятельно не рекомендовал вам делать это, так как это не очень похоже на c ++.

Лучшим решением было бы создать собственную иерархию классов для хранения указателей разных типов, например:

class DistributionBase {
public:
virtual ~DistributionBase() {}
}

template<typename T>
class Distribution : public DistributionBase {
public:
typedef T DistributionType;
T* distribution;

Distribution(T* d) : distribution(d) {}
~Distribution() { delete distribution; }
}template<typename T>
Distribution<T>* make_distribution(T* d) {
return new Distribution<T>(d);
}

И тогда вы можете использовать его следующим образом:

std::vector<DistributionBase*> distributions;
distributions.push_back(make_distribution(new boost::math::normal_distribution<double>(n)))
distributions.push_back(make_distribution(new boost::math::students_t_distribution<float>(n)))

Проблема в том, что вам нужно где-то хранить тип дистрибутива, чтобы вы могли static_cast исправить тип:

boost::math::normal_distribution<double>* d = static_cast< Distribution<boost::math::normal_distribution<double> > >(distributions[0])->distribution;

Это всего лишь фрагмент, который должен показать вам точку зрения, а не полный пример.

0

Вы можете обернуть указатели вокруг вашего общего базового класса. Здесь я буду использовать шаблон Template Pattern:

class Distribution {
public:
double pdf( double d) { doPdf( d)};
private:
virtual double doPdf( double d) {} = 0;
virtual ~Distribution() {}
};

class NormalDistribution : public Distribution {
private:
boost::math::normal_distribution<double> nd;
double doPdf( double d) { return pdf( nd, d);}
};

class StudentsTDistribution : public Distribution {
private:
boost::math::students_t_distribution<double> std;
double doPdf( double d) { return pdf( std, d);}
};

использование:

std::vector< boost::shared_ptr<Distribution> > v;
v.push_back( boost::make_shared<NormalDistribution>());
v.push_back( boost::make_shared<StudentsTDistribution>());
v[0]->pdf( 0.5); // draw from Gauss's distribution
v[1]->pdf( 0.5); // draw from fatter tails - t Student distribution
0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector