как typedef

я есть вопрос относительно частичной специализации шаблонных функций-членов.

Фон: Цель состоит в том, чтобы вычислить описательную статистику больших наборов данных, которые слишком велики для одновременного хранения в памяти. Поэтому у меня есть классы аккумуляторов для дисперсии и ковариации, где я могу вставлять наборы данных по частям (либо по одному значению за раз, либо в больших кусках). Довольно упрощенная версия, вычисляющая только среднее арифметическое

class Mean
{
private:
    std::size_t _size;
    double _mean;
public:
    Mean() : _size(0), _mean(0)
    {
    }
    double mean() const
    {
        return _mean;
    }
    template <class T> void push(const T value)
    {
        _mean += (value - _mean) / ++_size;
    }
    template <class InputIt> void push(InputIt first, InputIt last)
    {
        for (; first != last; ++first)
        {
            _mean += (*first - _mean) / ++_size;
        }
    }
};

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

Проблема: Это прекрасно работает для всех типов данных. Однако классы-аккумуляторы должны уметь обрабатывать и комплексные числа, сначала вычисляя абсолютное значение | z | а затем подтолкнуть его к аккумулятору. Для нажатия отдельных значений легко предоставить перегруженный метод

template <class T> void push(const std::complex<T> z)
{
    T a = std::real(z);
    T b = std::imag(z);
    push(std::sqrt(a * a + b * b));
}

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

// default version for all integral types
template <class InputIt, class T>
struct push_impl
{
    static void push(InputIt first, InputIt last)
    {
        for (; first != last; ++first)
        {
            _mean += (*first - _mean) / ++_size;
        }
    }
};

// specialised version for complex numbers of any type
template <class InputIt, class T>
struct push_impl<InputIt, std::complex<T>>
{
    static void push(InputIt first, InputIt last)
    {
        for (; first != last; ++first)
        {
            T a = std::real(*first);
            T b = std::imag(*first);
            _mean += (std::sqrt(a * a + b * b) - _mean) / ++_size;
        }
    }
};

В классе аккумулятора шаблонные методы структуры делегирования затем вызываются

template <class InputIt>
void push(InputIt first, InputIt last)
{
    push_impl<InputIt, typename std::iterator_traits<InputIt>::value_type>::push(first, last);
}

Однако есть одна проблема с этой техникой, которая заключается в том, как получить доступ к закрытым членам класса аккумулятора. Поскольку это разные классы, прямой доступ невозможен, и, кроме того, методы push_impl должны быть статическими и не могут обращаться к нестатическим элементам аккумулятора.

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

Создать экземплярpush_impl в каждом звонкеОт себя с (возможным) снижением производительности из-за дополнительной копии.Иметь экземплярpush_impl как переменная-член класса аккумулятора, что помешало бы мне вставлять разные типы данных в аккумулятор, поскольку экземпляр должен был быть полностью специализированным.Сделайте все члены класса аккумулятора публичными и передайте*это вpush_impl::От себя() звонки. Это особенно плохое решение из-за нарушения инкапсуляции.Реализуйте версию итератора в терминах версии с одним значением, т.е. вызовитеОт себя() метод для каждого элемента с (возможным) снижением производительности из-за дополнительного вызова функции.

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

Одно решение предпочтительнее других или я что-то упускаю?

С наилучшими пожеланиями и большое спасибо.

Ответы на вопрос(3)

Ваш ответ на вопрос