我有一个Matrix类
template<typename Type, size_t Rows, size_t Columns> class Matrix
{
public:
Type Entries[Rows][Columns];
// Operators, etc.
};
然后我需要对这个类进行特化(对于方形矩阵等),这意味着我需要为每个特化重写整个类的实现。然后我开始考虑避免代码重复的方法。
我想到的第一个想法是在基本模板中定义专门的函数,但删除它们,以便它们不能在基本模板中使用,并在类之外定义特化。
// Inside class definition
auto GetDeterminant() const -> T = delete;
// Outside class definition
template<typename Type, size_t Size> Matrix<T, Size, Size>::GetDeterminant() const -> T
{
// Implementation
}
然而,在遇到一些错误之后,我了解到不允许部分专门化的功能模板。所以我尝试了这些方法
方法1:
template<typename Derived, typename Type, size_t Rows, size_t Columns> class MatrixBase
{
public:
T Entries[Row][Columns];
// Operators, etc.
}
template<typename Type, size_t Rows, size_t Columns> class Matrix : public MatrixBase<Matrix<Type, Rows, Columns>, Rows, Columns>
{
// General MxN matrix stuff
}
template<typename Type, size_t Size> class Matrix<T, Size, Size> : public MatrixBase<Matrix<T, Size, Size>, Size, Size>
{
public:
// Specific MxM matrix stuff
}
这里我实现了 CRTP ,就像在另一个question上看到的那样,但是我觉得抽象有点不需要,尽管可能有更好的方法。所以我提出了另一种方法。
方法2:
template<typename T, size_t Rows, size_t Columns> class Matrix
{
public:
T Entries[Rows][Columns]
auto GetDeterminant() const -> T
{
static_assert(Rows == Columns);
// Some computations
}
}
这似乎很好,如果有人试图在非方阵上使用它,就会出现编译时错误。
问题:
有没有办法只使用模板来解决这个问题?或者我应该坚持使用其中一种方法?
答案 0 :(得分:0)
我建议使用SFINAE启用或禁用GetDeterminant()
template <typename Type, size_t Rows, size_t Columns>
class Matrix
{
public:
Type Entries[Rows][Columns];
template <size_t R = Rows>
typename std::enable_if<R == Columns, Type>::type GetDeterminant () const
{
// some computations
return {};
}
};
如果您想避免GetDeterminant()
可能导致明确模板R
值的风险如下
Matrix<int, 3, 3> m0;
Matrix<long, 3, 2> m1;
auto d0 = m0.GetDeterminant(); // compile
//auto d1 = m1.GetDeterminant(); // compilation error
auto d1 = m1.GetDeterminant<2>(); // compile!!!
您可以维护static_assert
(类似static_assert(R == Rows, "!");
)或者您可以改进std::enable_if
测试,如下所示
template <size_t R = Rows>
typename std::enable_if<(R == Columns) && (R == Rows), Type>::type
GetDeterminant () const
{
// some computations
return {};
}
答案 1 :(得分:0)
首先,引用aschelper的评论:
我喜欢
static_assert
方法。
我也是。它清晰,简洁,可以给出有意义的解释错误信息。
struct
(在实现命名空间中)namespace matrix_impl {
template<std::size_t Rows, std::size_t Columns>
struct determinate_calculation {
int operator()(int const (& entries)[Rows][Columns]) const = delete;
};
// or just don't define the class template
template<std::size_t Size>
struct determinate_calculation<Size, Size> {
int operator()(int const (& entries)[Size][Size]) const {
return entries[0][0]; // Woah, calculation wrong as hell ;)
}
};
}
template<std::size_t Rows, std::size_t Columns>
struct Matrix {
int entries[Rows][Columns];
int get_determinate() const {
return matrix_impl::determinate_calculation<Rows, Columns>{}(entries);
}
};
Matrix
类的必需成员作为参数传递给函数。好,因为它使依赖显式。很糟糕,因为有很多成员,这可能会变得很麻烦(“嘿,让我们使用一个类来进行参数传递”只是添加到样板文件中)。std::enable_if
max66 was faster,无需再添加。