我有一个关于模板化成员函数的部分专业化的问题。
背景:目标是计算大型数据集的描述性统计数据,这些数据集太大而无法一次保存在内存中。因此,我有方差和协方差的累加器类,我可以在其中逐个推入数据集(一次一个值或更大的块)。仅计算算术平均值的相当简化的版本是
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;
}
}
};
这种累加器类的一个特殊优点是可以将不同数据类型的值推入同一累加器类。
Problem:这适用于所有整数数据类型。然而,累加器类应该能够通过首先计算绝对值 |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由于额外的副本而(可能)导致性能下降。
- 有一个实例推送实现作为累加器类的成员变量,这将阻止我将不同的数据类型推入累加器,因为实例必须完全专业化。
- 将累加器类的所有成员公开并通过*this to 推送实现::push()来电。由于封装的破坏,这是一个特别糟糕的解决方案。
- 根据单值版本实现迭代器版本,即调用push()由于额外的函数调用,每个元素的方法(可能)会降低性能。
请注意,上述性能下降本质上是理论上的,由于编译器巧妙的内联,可能根本没有问题,但实际情况push方法可能比上面的示例复杂得多。
一种解决方案比其他解决方案更好还是我错过了什么?
致以最诚挚的问候和非常的感谢。