C++ 不同的模板类实现,但成员函数相同

C++ 不同的模板类实现,但成员函数相同,c++,c++11,templates,template-specialization,class-template,C++,C++11,Templates,Template Specialization,Class Template,我有一个模板矩阵类,其中包含一些典型的成员函数,如逆,行列式,运算符*,等等。。我想在固定大小矩阵和动态大小矩阵的模板实现中重用这些成员函数的代码。 这可能吗?如果是,怎么做? 在下面的代码中,与Eigen一样,我使用“-1”表示动态尺寸。是的,我知道我可以用图书馆来做这个,但对于我的申请来说,这是不可行的。由于应用程序(CUDA)的性质,无法实现标准功能 是否可以根据模板参数生成具有不同成员变量大小的模板类?例如,当Dynamic\u Rows=-1和Dynamic\u Cols=-1时,数据

我有一个模板矩阵类,其中包含一些典型的成员函数,如
行列式
运算符*
,等等。。我想在固定大小矩阵和动态大小矩阵的模板实现中重用这些成员函数的代码。 这可能吗?如果是,怎么做?

在下面的代码中,与Eigen一样,我使用“
-1
”表示动态尺寸。是的,我知道我可以用图书馆来做这个,但对于我的申请来说,这是不可行的。由于应用程序(CUDA)的性质,无法实现标准功能

是否可以根据模板参数生成具有不同成员变量大小的模板类?例如,当
Dynamic\u Rows=-1
Dynamic\u Cols=-1
时,数据仅为
T**data
,否则其
T data[Rows][Cols]

到目前为止,我有一个用于动态大小矩阵的模板类(“下面的最小”示例,请注意,代码容易出错,因为我对“高级”类模板比较熟悉)

但是在固定大小矩阵实例化的情况下,我希望有一个固定大小的数据成员变量

template<class T, int Dynamic_Rows, int Dynamic_Cols>
class CMatrix<T, -1, -1>
{
private:
   size_t n_rows, n_cols;
   T** data;

   void allocate_data();
   void deallocate_data();

public:
   CMatrix(const size_t n_rows, const size_t n_cols);
   CMatrix(const CMatrix& other);
   ~CMatrix();
   CMatrix& operator=(const CMatrix& rhs);
   CMatrix exp() const;
};
我现在唯一能想到的是,允许动态和固定大小的矩阵,基本上实现类的另一个模板

template<class T, size_t Rows, size_t Cols>
class CMatrix<T, Rows, Cols>
{
private:
   size_t n_rows = Rows, n_cols = Cols;
   T data[Rows][Cols];

public:
   CMatrix() {}
   CMatrix(const CMatrix& other);
   CMatrix& operator=(const CMatrix& rhs);
   CMatrix exp() const;
};
模板
类CMatrix
{
私人:
大小n行=行,n列=列;
T数据[行][Cols];
公众:
CMatrix(){}
CMatrix(常数CMatrix&其他);
CMatrix和运算符=(常数CMatrix和rhs);
CMatrix exp()常数;
};
使用可变模板

template<class T, int...> class CMatrix;
模板类CMatrix;
但那只会复制我的大多数成员函数的代码

有没有可能……[…]当
动态行=-1
动态列=-1
时,数据就是
T**data
,否则它的
T数据[行][Cols]

您可以为
CMatrix

template<typename T, int Rows, int Cols> struct type_helper final {
    static_assert(Rows >= 0 && Cols >= 0, "Rows and COls must be valid!");
    using type = T[Rows][Cols];
};

template<typename T> struct type_helper<T, -1, -1>  final {
    using type = T**;
};
template struct type\u helper final{
静态断言(行>=0&&Cols>=0,“行和列必须有效!”);
使用type=T[行][Cols];
};
模板结构类型\u helper final{
使用类型=T**;
};
现在在课堂上

template<class T, int Dynamic_Rows = -1, int Dynamic_Cols = -1>
class CMatrix /* final */
{
    // get the member Type like this!
    using Type = typename type_helper<T, Dynamic_Rows, Dynamic_Cols>::type;

    Type data;
public:

};
模板
类别CMatrix/*最终*/
{
//像这样获取成员类型!
使用Type=typename Type\u helper::Type;
类型数据;
公众:
};
()

您可以使用它根据某个编译时常量选择一种或另一种类型,而无需复制类

然而,我曾经做过类似的事情,发现使用固定大小的数据结构(
std::array
而不是
std::vector
)在实际用例中几乎没有改进。 固定大小数组(
std::array
type[fixed_size]
)的一个非常大的优点是编译器能够优化很多操作,但如果数组很大,则好处可以忽略不计

也应该考虑N.代词“m.”的评论——特别是如果你使用的是GPU(它们往往需要在内存中连续的数据结构)。不要使用
T**data
,而是使用单个
std::vector
,以及附加的
std::array
作为每个维度的大小。然后,您可以使用以下内容转换索引:

using Indices = std::array<size_t, number_of_dimensions>;

template <unsigned int n = number_of_dimensions>
static inline size_t indices_to_offset(Indices const & indices,
                                       Indices const & dimensions){
    if constexpr (n == 0) return 0;
    else {
        size_t const next_index_number = indices[n-1];
        assert(next_index_number < dimensions[n-1]);
        return next_index_number +
            dimensions[n-1]*indices_to_offset<n-1>(indices, dimensions);
    }
}

template <unsigned int n = number_of_dimensions>
static inline Indices offset_to_indices(size_t const offset,
                                        Indices const & dimensions,
                                        Indices indices = Indices()){
    if constexpr (n == 0) return indices;
    else {
        size_t const fast_dimension_size = dimensions[n-1];
        indices[n-1] = offset % fast_dimension_size;
        return offset_to_indices<n-1>(offset/fast_dimension_size , dimensions, indices);
    }
}
使用索引=std::数组;
模板
静态内联大小索引到偏移量(索引常量和索引,
指数(常数和尺寸){
如果constexpr(n==0)返回0;
否则{
大小常数下一个索引数=索引[n-1];
断言(下一个索引编号<维度[n-1]);
返回下一个索引号+
尺寸[n-1]*索引到偏移(索引,尺寸);
}
}
模板
静态内联索引偏移到索引(大小常数偏移,
指数常数和尺寸,
索引=索引(){
如果constexpr(n==0)返回索引;
否则{
尺寸常数快速尺寸=尺寸[n-1];
索引[n-1]=快速维度大小的偏移百分比;
将偏移量返回到索引(偏移量/快速维度大小、维度、索引);
}
}

这是使用CRTP进行此类专门化的一种非常通用的方法。它没有;不要求有一个简单的单一条件可以用来选择一个数据成员的实现,其余条件保持不变

template <class T, class Impl> class MatrixBase {

   // data is NOT defined here
   Impl exp() const
   {
        Impl& self = static_cast<Impl&>(*this);             // <-- this is magic
        Impl result = self;                                 // <-- this is magic
        for (size_t i = 0; i < n_rows; i++)
        {
            for (size_t j = 0; j < n_cols; j++)
            {
                result.data[i][j] = exp(result.data[i][j]); // <-- data is defined in Impl
            }
        }
        return result;
    }
    // other functions that do not depend on actual type of data
};

template <class T>
class DynamicMatrix : public MatrixBase<T, DynamicMatrix<T>> {
    T** data;
    // define constructors etc here
};

template <class T, int Rows, int Cols>
class StaticMatrix : public MatrixBase<T, StaticMatrix<T, Rows, Cols>> {
    T[Rows][Cols] data;
    // define constructors etc here
};

将我的评论转换为答案(使用
向量
而不是
矩阵
):

拆分类,这样您就有了专门用于特定类的部分,以及公共部分,比如:

// helper to allow variadic constructor without pitfall.
constexpr struct in_place_t{} in_place{};

template <typename T, std::size_t DIM>
class VectorData
{
protected: // You might even make it private and give the common accessor in public part
           // To avoid to use interface not shared with std::vector
    std::array<T, DIM> data;
public:
    VectorData() = default;

    template <typename ... Ts>
    VectorData(in_place_t, Ts&&... args) : data{std::forward<Ts>(args)...} {}
};

template <typename T>
class VectorData<T, std::size_t(-1)>
{
protected: // You might even make it private and give the common accessor in public part
           // To avoid to use interface not shared with std::array
    std::vector<T> data;
public:
    explicit VectorData(std::size_t size) : data(size) {}

    template <typename ... Ts>
    VectorData(in_place_t, Ts&&... args) : data{std::forward<Ts>(args)...} {}
};
//帮助程序允许变量构造函数无陷阱。
constexpr struct in_place_t{}in_place{};
模板
类向量数据
{
protected://您甚至可以将其设置为私有,并将公共访问器设置为公共部分
//避免使用未与std::vector共享的接口
std::数组数据;
公众:
VectorData()=默认值;
模板
VectorData(就地、Ts&…args):数据{std::forward(args)…}{
};
模板
类向量数据
{
protected://您甚至可以将其设置为私有,并将公共访问器设置为公共部分
//避免使用未与std::array共享的接口
std::矢量数据;
公众:
显式向量数据(std::size\u t size):数据(size){
模板
VectorData(就地、Ts&…args):数据{std::forward(args)…}{
};
然后,公共部分:

template <typename T, std::size_t DIM = std::size_t(-1)>
class Vector : public VectorData<T, DIM>
{
public:
    using VectorData<T, DIM>::VectorData;

    Vector(std::initializer_list<T> ini) : VectorData<T, DIM>(in_place, ini) {}

#if 1 // Those might go in `VectorData` to avoid to use `data` directly
    std::size_t size() const { return this->data.size(); }
    T& operator[](std::size_t i) { return this->data[i]; }
    const T& operator[](std::size_t i) const { return this->data[i]; }
#endif

    // Example of methods which don't care underlying container
    template <std::size_t DIM2>
    Vector& operator += (const Vector<T, DIM2>& rhs) {
        assert(this->size() == rhs.size());
        
        for (std::size_t i = 0; i != this->size(); ++i) {
            (*this)[i] += rhs[i];
        }
        return *this;
    }

    template <std::size_t DIM2>
    friend Vector operator + (Vector lhs, const Vector<T, DIM2>& rhs) {
        return lhs += rhs;
    }
};
模板
类向量:公共向量数据
{
公众:
使用VectorData::VectorData;
向量(std::initializer\u list ini):向量数据(in\u place,ini){}
#如果1//,则可能将其放在“VectorData”中,以避免直接使用“data”
std::size\u t size()常量{返回此-
// helper to allow variadic constructor without pitfall.
constexpr struct in_place_t{} in_place{};

template <typename T, std::size_t DIM>
class VectorData
{
protected: // You might even make it private and give the common accessor in public part
           // To avoid to use interface not shared with std::vector
    std::array<T, DIM> data;
public:
    VectorData() = default;

    template <typename ... Ts>
    VectorData(in_place_t, Ts&&... args) : data{std::forward<Ts>(args)...} {}
};

template <typename T>
class VectorData<T, std::size_t(-1)>
{
protected: // You might even make it private and give the common accessor in public part
           // To avoid to use interface not shared with std::array
    std::vector<T> data;
public:
    explicit VectorData(std::size_t size) : data(size) {}

    template <typename ... Ts>
    VectorData(in_place_t, Ts&&... args) : data{std::forward<Ts>(args)...} {}
};
template <typename T, std::size_t DIM = std::size_t(-1)>
class Vector : public VectorData<T, DIM>
{
public:
    using VectorData<T, DIM>::VectorData;

    Vector(std::initializer_list<T> ini) : VectorData<T, DIM>(in_place, ini) {}

#if 1 // Those might go in `VectorData` to avoid to use `data` directly
    std::size_t size() const { return this->data.size(); }
    T& operator[](std::size_t i) { return this->data[i]; }
    const T& operator[](std::size_t i) const { return this->data[i]; }
#endif

    // Example of methods which don't care underlying container
    template <std::size_t DIM2>
    Vector& operator += (const Vector<T, DIM2>& rhs) {
        assert(this->size() == rhs.size());
        
        for (std::size_t i = 0; i != this->size(); ++i) {
            (*this)[i] += rhs[i];
        }
        return *this;
    }

    template <std::size_t DIM2>
    friend Vector operator + (Vector lhs, const Vector<T, DIM2>& rhs) {
        return lhs += rhs;
    }
};