只更改一个成员函数的类模板专用化

Class template specialization that changes only one member function

本文关键字:函数 专用 成员 一个      更新时间:2023-10-16

我有一个类模板Function,它将一个无符号整数作为输入数的模板参数。该模板重载operator(),因此可以针对一组给定的输入来评估Function

通常,该成员的原型之一是operator()(double, ...)。但是,如果模板参数为0,则该原型将不起作用,因为它至少需要一个参数。

template <unsigned Arity>
struct Function {
void operator () (double, ...);
};

通常,我只会编写一个模板专用化,但会有很多冗余代码,因为还有很多其他成员函数。同样,通常情况下,我会创建一个基类,其中包含主类定义和要继承的专门化的冗余代码。

struct FunctionBase {
// Common code
Function operator + (Function const &) const; // ?
};
template <unsigned Arity>
struct Function : FunctionBase { /* etc */ };

不幸的是,我不确定如何执行此操作,因为例如operator+意味着返回Function。但是,如果以后才定义Function,它怎么能做到这一点呢?Function继承了基类,通过这种设计,operator+在基类中。。。

它可以返回基类的一个实例,但我们需要一种方法将该实例转换为Function的实例,我知道如果不复制第一个实例的数据,就无法做到这一点,这在性能方面非常昂贵。

我怎样才能做到这一点?

这个问题很难回答,因为它还很不清楚
以下两种可能的解决方案:

  • 如果你想继续使用Arity模板参数,你可以使用sfinae'd运算符来处理等于0:的Arity

    #include<iostream>
    template<int Arity>
    struct Function {
    template<int N = Arity>
    std::enable_if_t<N == 0> operator()() {
    std::cout << "arity == 0" << std::endl;
    }
    template<int N = Arity>
    std::enable_if_t<N != 0> operator()(double, ...) {
    std::cout << "arity != 0" << std::endl;
    }
    };
    int main() {
    Function<0> f1;
    Function<2> f2;
    f1();
    f2(0., 42);
    }
    

    这样,您就不再需要引入基类,所有相关的问题也不再适用。

  • 如果你介意改变方法,你可以为你的函数对象切换到以下模式:

    template<typename>
    struct Function;
    template<typename R, typename... A>
    struct Function<R(A...)> {
    R operator()(A... args) {
    // ...
    }
    // ...
    };
    

    您可以按如下方式使用它:

    Function<void(int, char)> f;
    

    如果你想有一个固定的double作为operator()的第一个参数,你可以这样做:

    template<typename R, typename... A>
    struct Function<R(double, A...)> {
    R operator()(double d, A... args) {
    // ...
    }
    // ...
    };
    

    并按如下方式使用:

    Function<void(double, int, char)> f1;
    Function<void(double)> f1;
    

    这将至少有助于轻松处理空参数包(注意,sizeof...(A)在任何情况下都会向您返回提交的参数数量)。

    它遵循一个最小的工作示例实现:

    #include<iostream>
    template<typename>
    struct Function;
    template<typename R, typename... A>
    struct Function<R(A...)> {
    R operator()(A... args) {
    int _[] = { 0, (std::cout << args << std::endl, 0)... };
    (void)_;
    }
    template<typename... O>
    Function<R(A..., O...)> operator+(Function<R(O...)>) {
    return {};
    }
    // ...
    };
    int main() {
    Function<void(int)> f1;
    Function<void(double)> f2;
    f1(42);
    f2(0.);
    (f1+f2)(3, .3);
    }