c – 部分成员函数模板特化和数据成员访问

我有一个关于模板化成员函数的部分特化的问题.

背景:目标是计算大型数据集的描述性统计数据,这些数据集太大而无法一次保存在内存中.因此,我有方差和协方差的累加器类,我可以逐个推入数据集(一次一个值或更大的块).仅计算算术平均值的相当简化的版本

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的实例作为累加器类的成员变量,这将阻止我将不同的数据类型推入累加器,因为实例必须完全专用.
>使累加器类的所有成员都公开,并将* this传递给push_impl :: push()调用.由于封装中断,这是一个特别糟糕的解决方案.
>根据单值版本实现迭代器版本,即为每个元素调用push()方法,由于额外的函数调用,可能会降低性能.

请注意,所提到的降低性能本质上是理论上的,并且由于编译器的巧妙内联可能完全没有问题,但是实际的推送方法可能比上面的示例复杂得多.

一种解决方案比其他解决方案更好还是我错过了什么?

最好的问候和非常感谢.

最佳答案 如评论所述,您根本不需要使用部分特化,实际上部分特化通常很容易避免,并且首选避免.

private:
template <class T>
struct tag{}; // trivial nested struct

template <class I, class T> 
void push_impl(I first, I last, tag<T>) { ... } // generic implementation

template <class I, class T>
void push_impl(I first, I last, tag<std::complex<T>>) { ... } // complex implementation

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

由于push_impl是(私有)成员函数,因此您不再需要执行任何特殊操作.

与您提出的解决方案相比,这没有额外的性能成本.它的函数调用次数相同,唯一的区别是按值传递无状态类型,这对编译器来说是一个完全无关紧要的优化.并且封装也没有牺牲.并且稍微少了样板.

点赞