我有一个关于模板成员函数部分特化的问题。
背景:目标是计算大型数据集的描述性统计信息,这些数据集太大而无法一次性存储在内存中。因此,我有方差和协方差的累加器类,可以逐个推入数据集片段(可以一次推入一个值或更大的块)。一个相当简化的版本仅计算算术平均数。
在累加器类中,委托结构的模板方法随后被调用。
然而,这种技术存在一个问题,即如何访问累加器类的私有成员。由于它们是不同的类,因此无法直接访问,并且push_impl方法需要是静态的,无法访问累加器的非静态成员。
我可以想到以下四种解决方案,它们都有各自的优缺点: 1.在每次调用push时创建push_impl的实例(可能)会降低性能。 2.将push_impl的实例作为累加器类的成员变量,这将防止我将不同的数据类型推入累加器,因为该实例必须完全专门化。 3.使累加器类的所有成员公开,并将 *this 传递给 push_impl::push() 调用。这是一种特别糟糕的解决方案,因为会破坏封装性。 4.以单个值版本为基础实现迭代器版本,即对每个元素调用push()方法(可能)会降低性能,因为需要额外的函数调用。
请注意,提到的性能下降是理论上的,可能根本不存在任何问题,因为编译器会进行巧妙的内联,但实际的push方法可能比上面的示例复杂得多。
是否有一种解决方案比其他解决方案更可取,或者我忽略了一些东西?
最好的问候并致以感谢。
背景:目标是计算大型数据集的描述性统计信息,这些数据集太大而无法一次性存储在内存中。因此,我有方差和协方差的累加器类,可以逐个推入数据集片段(可以一次推入一个值或更大的块)。一个相当简化的版本仅计算算术平均数。
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方法需要是静态的,无法访问累加器的非静态成员。
我可以想到以下四种解决方案,它们都有各自的优缺点: 1.在每次调用push时创建push_impl的实例(可能)会降低性能。 2.将push_impl的实例作为累加器类的成员变量,这将防止我将不同的数据类型推入累加器,因为该实例必须完全专门化。 3.使累加器类的所有成员公开,并将 *this 传递给 push_impl::push() 调用。这是一种特别糟糕的解决方案,因为会破坏封装性。 4.以单个值版本为基础实现迭代器版本,即对每个元素调用push()方法(可能)会降低性能,因为需要额外的函数调用。
请注意,提到的性能下降是理论上的,可能根本不存在任何问题,因为编译器会进行巧妙的内联,但实际的push方法可能比上面的示例复杂得多。
是否有一种解决方案比其他解决方案更可取,或者我忽略了一些东西?
最好的问候并致以感谢。