部分模板专业化而不重写类

时间:2016-04-17 16:03:00

标签: c++ templates c++11

我正在编写一个矩阵类,它是一个模板类:

template <class T, int height, int width>

我想编写计算矩阵行列式的成员函数。如何在不为方矩阵编写完整的单独模板类的情况下,仅为方形矩阵声明此函数?

2 个答案:

答案 0 :(得分:6)

由于矩阵类是在heightwidth上模板化的,因此方阵是一个矩阵,其模板参数相等。对于第二个和第三个模板参数,您可以使用相同的N轻松地将此类作为参数:

template <class T, int N>
auto get_det(const Matrix<T, N, N>& mat) -> float;

在您的情况下,这将是最简单的解决方案。但是,您也可以使用enable_if执行此操作,但在此特定情况下,它可能会过度。但是,您可以将其应用于其他一些更复杂的解决方案:

template <class T, int H, int W>
auto get_det(const Matrix<T, H, W>& mat) -> std::enable_if_t<H == W, float>;

如果您想详细了解SFINAE和enable_if

,请检查enable_if documentationC++ Idioms/enable-if

如果你想要一个方法,不幸的是,事情并不那么容易。您可以专门从一个类中专门设置一个方法,但不能局部专门化它。然而,有一个技巧可以解决它:

template <class T, int H, int W>
struct Matrix {

  template <int N = H, int M = W>
  auto get_det() const -> std::enable_if_t<N == M, float> { return 2.4f; }
};

你可以这样测试:

Matrix<int, 2, 3> not_square;
Matrix<int, 2, 2> square;

// error error: no matching member function for call to 'get_det'
// note: candidate template ignored: disabled by 'enable_if' [with N = 2, M = 3]
//not_square.get_det();

//OK:
square.get_det();

此处get_det需要模板化,因为enbale_if必须对成员模板参数进行操作。否则,如果它将对类模板参数(enable_if_t<H == W>)进行操作,它将禁用整个类,而不仅仅是成员。

一个小缺点是您可以明确地专门化该方法,从而允许在非方形矩阵上调用它。但这只是不应该做的事情,通常不是:

// No compilation error:
not_square.get_det<2,2>();

方法的另一个技巧是使用自由函数:

template <class T, int H, int W>
struct Matrix;

namespace detail {
template <class T, int N>
auto get_det(const Matrix<T, N, N>&) -> float { return 3.5; }
}

template <class T, int H, int W>
struct Matrix { 
  auto get_det2() const -> float { return detail::get_det(*this); }
};

老实说,我认为这不会起作用,但clang和gcc都表现得很好。仍然不是100%肯定它是好的。我有空的时候会调查。

答案 1 :(得分:3)

它可能并不总是可行,但我认为最简单(或至少最容易阅读/理解)的解决方案是编写一个包含所有默认方法的公共基类。实际模板只继承此类,否则为空,而parital(或完整)特化添加其他的mehtods:

template<class T, size_t M, size_t N>
struct BaseMatrix{
        T& operator()(size_t r, size_t c) {
            return  _e[r*N+c];
        }

    protected:
        BaseMatrix() =default;
        ~BaseMatrix() = default;

    private:
        T _e[M*N];     
};

template<class T, size_t M, size_t N>
struct Matrix: BaseMatrix<T,M,N> {
};

template<class T, size_t M>
struct Matrix<M,M>: BaseMatrix<T,M,M> {   
    T det(){return 42;}
};