совет для статического шаблона класса - PullRequest
4 голосов
/ 19 июля 2011

У меня есть эта проблема (гистограммирование).У меня есть реальное пространство: [a,b] каким-то образом разделено ([a0=a, a1, a2, ..., b]).Разделение может быть с равным пространством (a1 - a0 = a2 - a1 = ...) или переменными.

Мне нужен класс, который обрабатывает это, с некоторыми методами, чтобы сказать, учитывая значение, к которому принадлежит элемент bin раздела;другие методы, чтобы найти центр определенной корзины и многое другое.

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

Binner binner(binning);
binner.get_bin(1.3);
binner.get_centerbin(2);

, поэтому я попыталсянаписать статический класс, используя шаблон, чтобы сделать что-то подобное:

Binner<binning>::get_bin(1.3);
Binner<binning>::get_centerbin(2);

Это хорошая идея?Есть другой способ сделать это?Теперь у меня есть бесплатные функции, такие как

double get_bin(double bin, Binning binning); // a lot of if/else inside

, но я думаю, что это слишком подвержено ошибкам.

Вот моя реализация:

enum Binning {CELL, LARGE, BE};
const double binning_LARGE[] = {0, 1.2, 1.425, 1.550, 1.800, 2.5};
const double binning_BE[] =  {0, 1.425, 1.550, 2.5};

template<Binning binning>
class Binner
{
public:
    static const double* bins;
    static const int n;
    static int get_bin(double value);
};

template<> const double* myclass<LARGE>::bins = binning_LARGE;
template<> const double* myclass<BE>::bins = binning_BE;

template<> const int myclass<LARGE>::n = sizeof(binning_LARGE) / sizeof(double);
template<> const int myclass<BE>::n = sizeof(binning_BE) / sizeof(double);

template<Binning binning> int myclass<binning>::get_bin(double value)
{
    return find_if(bins, bins + n,
           bind2nd(greater<double>(), value)) - bins - 1;
}

template<> int myclass<CELL>::get_bin(double value)
{
    return static_cast<int>(value / 0.025);
}
  1. это хорошореализация / дизайн?
  2. Есть ли способ избежать использования поля n с помощью std::vector?Как?
  3. Есть ли способ параметризации 0.025?Я знаю, что double не может быть параметром шаблона, но я могу написать что-то похожее на это:

    Binner<0.025> binner;
    
  4. other / advices?

Редактировать:

Третий пункт. Почему я не могу этого сделать:

template<Binning binning, int N=100>
class Binner
{
public:
    static const double* bins;
    static const int n;
    static int bin(double value);
};

...

template<Binning binning, int N> int Binner<CELL, N>::bin(double value)
{
    return static_cast<int>(value / (2.5 / N));
}

Ответы [ 3 ]

4 голосов
/ 19 июля 2011

ИМХО, ваш дизайн в порядке, если вы не хотите создавать экземпляр класса. Действительно, мне кажется, что-то вроде шаблонного метапрограммирования . Имеет ли это смысл, зависит от того, как вы планируете повторно использовать этот шаблон.

Использование std :: vector позволит вам наверняка избавиться от переменной для хранения размера массива. Теперь, если это хорошо для вашего дизайна, я не знаю ... это переместит некоторую сложность из вашего определения шаблона в определение биннинга (которое теперь вы можете инициализировать очень просто) ...

Наконец, вы можете создать экземпляр шаблона, передав ему константу:

template < Binning binning, unsigned long N, unsigned long M>
class ... {
     <using N>
}
2 голосов
/ 19 июля 2011

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

Итак, я бы начал с поведения по умолчанию:

enum Binning {CELL, LARGE, BE};

template <Binning binning>
struct BinTraits
{
    // default behaviour
    int get_bin(double value) { return value / 0.025; } 
};

Тогда я бы предоставил специализации:

const double binning_LARGE[] = {0, 1.2, 1.425, 1.550, 1.800, 2.5};
const double binning_BE[] =  {0, 1.425, 1.550, 2.5};

template <typename RandomAccessCollectionT>
int get_bin_impl(double value, RandomAccessCollectionT collection, unsigned size)
{
    return find_if(collection, collection + size,
           bind2nd(greater<double>(), value)) - collection - 1;
}

template <>
struct BinTraits<LARGE>
{
    int get_bin(double value) { return get_bin_impl(value, binning_LARGE, sizeof(binning_LARGE) / sizeof(binning_LARGE[0])); } 
};

template <>
struct BinTraits<BE>
{
    int get_bin(double value) { return get_bin_impl(value, binning_BE, sizeof(binning_BE) / sizeof(binning_BE[0])); } 
};

Тогда я бы поместил фактическое поведение контейнера в другой класс, который требует поведения биннинга (назовем его HashTable):

template <typename BinTraits>
class HashTable
{
public:
    void insert(double value)
    {
        int bin = BinTraits::get_bin(value);
        _bins[bin].insert(value);
    }
    // _bin is a multimap or something
};
0 голосов
/ 19 июля 2011

Рассматривая использование find_if и bind2nd, а также функторов, кажется, что вы достаточно хорошо разбираетесь в STL и некоторых продвинутых концепциях C ++;все же то, что вы пытаетесь сделать, кажется чрезмерным.Хотя я не могу полностью понять, что вы пытаетесь сделать, но кажется, что вы могли бы полностью отказаться от шаблонов и использовать только класс (созданный с различными значениями) и параметры метода.

...