这可以用c++中的模板来完成吗?

Can this be accomplished with templates in C++?

本文关键字:c++      更新时间:2023-10-16

这是一个非常简单的类,表示三行双精度值,每行附加一些字符串信息:

struct ThreeRows {
  std::vector<double> row1;
  std::vector<double> row2;
  std::vector<double> row3;
  std::string row1info;
  std::string row2info;
  std::string row3info;
};

我想做的是用以下方式概括这种类型:

  1. 行不应再固定为3行,而应支持任意数量的行作为类型的一部分。

  2. 我应该能够指定什么类型应该在每一行。也许我想让double在第一行int在第二行。(使这个例子成为一个两行类)

  3. 最后,我应该能够附加其他信息,而不仅仅是string s到行。例如(继续第2点中的示例),我可能希望将string附加到第一行,但将自定义rss_feed附加到第二行。

如果模板允许这样做(实际上它们不允许),我想输入像这样的东西来获得我的类型:

Rows<{double,int}, {string,rss_feed}>

从中确定的行数,或者如果我真的必须这样做:

Rows<{double,int}, {string,rss_feed}, 2>

但这是不可能的

可以做些什么,如果可以,我该如何使用这个类模板?我该如何得到矢量和信息对象并处理它们呢?

使用一些模板魔法,你的问题可以通过可变模板和模板递归来解决。

template< size_t N, typename T, typename U, typename... Ts >
struct Rows : public Rows<N-1, Ts...> {
  vector<T> row;
  U rowinfo;
};
template<typename T, typename U>
struct Rows<1, T, U> {
  vector<T> row;
  U rowinfo;
};
Rows< 3, int, string, float, string, double, rss_feed > rows; 
=>  struct Rows {
       vector<int> v1;
       string s1;
       vector<foat> v2;
       string s2;
       vector<double> v3;
       rss_feed feed;
    }

要访问这些字段的值,可以实现一个模板化的get()成员函数

template< size_t N, typename T, typename U, typename... Ts >
struct Rows : public Rows<N-1, Ts...> {
  vector<T> row;
  U rowinfo;
  template< size_t M >
  typename enable_if< M == N, tuple<vector<T>&, U&> >::type
  get() {
    return make_tuple( ref(row), ref(rowinfo) );
  }   
};
template<typename T, typename U>
struct Rows<1, T, U> {
    vector<T> row;
    U rowinfo;
    template< size_t M >
    typename enable_if< M == 1, tuple<vector<T>&, U&> >::type
    get() {
        return make_tuple( ref(row), ref(rowinfo) );
    }
};

花点时间消化一下。get()方法返回一个元组,其中包含对请求字段的引用,并且只有当传入的数字与类的模板匹配时才启用,其他数字呢?我们可以像这样启用

template< size_t M >
typename enable_if< M != N, tuple<vector<T>&, U&> >::type
get() {
    return Rows<N-1, Ts...>::template get<M>();  // call parent's get,  
                                                 // ::template is required to avoid ambiguity
} 

但这是错误的!!返回类型不是tuple<vector<T>&, U&>,应该是tuple<vector<A>&, B&>,其中AB是可变模板Ts...

中的对应类型。

但是我们究竟如何从Ts...中得到我们想要的类型呢?我们可以用下面的技术找出可变模板的类型。

template< size_t N, typename... Ts >
struct variadic_type;
template< typename T, typename U, typename... Ts >
struct variadic_type< 0, T, U, Ts... > {
    typedef T T_type;
    typedef U U_type;
};
template< size_t k, typename T, typename U, typename... Ts >
struct variadic_type< k, T, U, Ts... > {
    typedef typename variadic_type< k-1, Ts... >::T_type T_type;
    typedef typename variadic_type< k-1, Ts... >::U_type U_type;
};

所以variadic_type< 1, int, int, short, short, float, float >::T_typeU_type将是shortshort

现在我们有了一种方法来找出可变模板上特定位置的类型,我们可以将它应用于错误的get()方法以获得正确的返回类型…

template< size_t M >
typename
    enable_if< M != N,
                tuple<
                    vector<typename variadic_type< N-M, T, U, Ts...>::T_type>&,
                    typename variadic_type< N-M, T, U, Ts...>::U_type&
                > >::type
get() {
    return Rows<N-1, Ts...>::template get<M>();
}

我们完成了,我们可以像

那样调用get()
Rows< 3, int, string, double, string, float, string > rows;
auto r3 = rows.get<3>();  //  { vector<int>& v, string& s };
auto r2 = rows.get<2>();  //  { vector<double>& v, string& s };
auto r1 = rows.get<1>();  //  { vector<float>& v, string& s };
auto r4 = rows.get<4>();  //  error:: other numbers will not compile!!

现在索引规则不是很好处理,但我认为这已经让大家明白了。

这可以很容易地使用std::tuple来指定类型列表。我们所需要做的就是声明主模板接受两个形参,然后创建一个局部特化,其中这些类型形参是元组。在部分专门化中,我们可以使用实参推导来捕获元组的模板形参,并根据我们的目的重用它们。我们可以创建一个新的模板来指定类型列表(即Types<int,double>),但是元组在这种情况下特别好,因为您需要有一种方法来访问单独的行,而std::tuple通过std::get<i>提供了一种内置的方法来实现这一点。使用元组作为模板参数可以使使用std::get访问行变得更加明显。

下面是一个完整的例子:

#include <string>
#include <tuple>
#include <vector>
// primary template
template <typename RowTuple,typename RowInfoTuple> struct Rows;
// variadic partial specialization 
template <typename... RowTypes,typename... RowInfoTypes>
struct Rows<std::tuple<RowTypes...>,std::tuple<RowInfoTypes...>>
{
  // use variadic expansion to make a tuple of vectors
  std::tuple<std::vector<RowTypes>...> rows;
  std::tuple<RowInfoTypes...> rowinfos;
};
struct rss_feed { };
int main(int,char**)
{
  Rows<
    std::tuple<double,int>,
    std::tuple<std::string,rss_feed>
  > data;
  std::get<0>(data.rows).push_back(1.5);
  std::get<1>(data.rows).push_back(2);
  std::get<0>(data.rowinfos) = "info";
  std::get<1>(data.rowinfos) = rss_feed();
  return 0;
}

如果你知道编译时的(有限的)类型,我会使用boost::variant来做这个:

typedef boost::variant<double, int> DataType;
typedef boost::variant<string, rss_feed> MetaDataType;
struct Row
{
    DataType data_;
    MetaDataType meta_data_;
};
template <int NUM_ROWS>
struct Rows
{
    Row row_data_[NUM_ROWS];
};

这并没有给你完全固定类型每行你似乎暗示,但它应该解决你的一般问题。

相关文章:
  • 没有找到相关文章