【问题标题】:Template specialization with Parameter Packs带有参数包的模板专业化
【发布时间】:2021-01-24 10:04:57
【问题描述】:

我对 c++ 很陌生,但想尝试做一些花哨的模板。我不确定这是否可能,但我相当有信心有办法实现这一目标。

所以问题来了: 我正在调用各种硬件函数,这些函数需要多个参数,并且只有一种参数类型不同:

int Read(HANDLE handle, int location, char* Name, int startindex1, int endindex1,
         int startindex2, int endindex2, int* rval, Astruct* callback) ;
int Read(HANDLE handle, int location, char* Name, int startindex1, int endindex1,
         int startindex2, int endindex2, double* rval, Astruct* callback) ;

硬件接口根据索引的值返回一个标量、数组或矩阵。我真正想要实现的是一个函数,它根据我传递的参数数量返回T 或vector<T> 或vector<vector<T>>。:

                T MyReadFunction<T>(HANDLE handle, int location, char* Name,int index)
        vector<T> MyReadFunction<T>(HANDLE handle, int location, char* Name,int startindex1,
                                                                            int endindex1   )
vector<vector<T>> MyReadFunction<T>(HANDLE handle, int location, char* Name,int startindex1, 
                                                                            int endindex1
                                                                            int startindex2, 
                                                                            int endindex2)

其中T 是基本类型,如int、real、float、double 等。

使用 3 个具有专业化的不同模板没有问题,但我很想以某种方式将它们结合起来。

我的假设是,这可以使用模板专业化来实现,但我无法理解它。我想我应该是这样的:

template<typename T ,int... Indexes>
T MyReadFunction (HANDLE handle, int location, char* Name, Indexes... myIndex){}

template<>
int MyReadFunction (HANDLE handle, int location, char* Name, int myindex)
{
    int rval = 0; 
    Read (handle,location,name,myindex,myindey,0,0,&rval, NULL) ;
    return rval;
}

这是一个两头野兽。我大概需要明确地实现我的 3 个案例以避免误用,但也想知道,我如何根据参数包的大小来实现模板专业化,参数包的大小不同。

我在 VS 2019 中使用最新的 msvc++

【问题讨论】:

    标签: c++ template-specialization parameter-pack


    【解决方案1】:

    您可以编写一个类型来创建所需等级的嵌套向量,如下所示:

    template<typename T,int N> 
    struct VT 
    { 
        using type = typename std::vector<typename VT<T, N - 1>::type>;
    };
    
    template<typename T> 
    struct VT<T, 0> 
    { 
        using type = int;
    };
    

    然后像这样写一个函数:

    template<typename T ,typename ...Indexes>
    typename VT<T, sizeof...(Indexes) / 2>::type 
      MyReadFunction (int handle, int location, char* Name, Indexes... myIndex) {}
    

    这是demo。

    正如您所观察到的,这样做并没有多大意义,因为函数体的实现会变得相当棘手。简单地编写特化或重载将是一个更简单的解决方案。

    请注意,如果您选择编写此函数模板,myIndex 参数包不受约束,即它将接受 int 以外的类型。当然,您可以通过编写更多代码来限制它。

    此外,此函数模板将接受大小为 3、5、7 等的 Indexes,这可能不是您想要的。同样,您也可以通过编写更多代码来限制它。

    【讨论】:

    • 值得一提的是,参数包的类型总是不同的。 typename ... Indexes 与问题中的int ... Indexes 不同。
    • @Potatoswatter 好点,添加了一些解释。
    • 我从你的答案中借用了另一个。我避免使用包来支持可变参数数组。顺便说一句,维度似乎是sizeof ... Indexes / 2 的(下限)。
    • @Potatoswatter 啊,关于维度的好消息。已编辑,谢谢。
    【解决方案2】:

    对于这个关于参数包的问题,​​cigien 有一个很好的答案。

    这是另一个不使用包的解决方案。参数包是类型列表,但实际上你不需要改变 N 种类型,你只需要一个 N 维数组。

    如果将索引列表放在花括号内,则可以将其视为具有编译时长度的列表。

    不过,在我看来,标量情况可能无法从多维情况中很好地概括。标量情况采用单个索引,而数组需要每个维度的开始和结束索引。

    template<typename T,int N> 
    struct VT 
    {
        using type = typename std::vector<typename VT<T, N - 1>::type>;
    };
    
    template<typename T> 
    struct VT<T, 0> 
    { 
        using type = T;
    };
    
    template< typename T, int n >
    std::enable_if_t< n == 1 || n % 2 == 0,
      typename VT<T, n/2>::type >
      MyReadFunction( HANDLE handle, int location, char* Name, int const (&indexes)[n] );
    
    // usage: MyReadFunction< double >( inputFile, 123, "name", { 1, 2, 3, 4 } );
    // yields vector< vector< double > >
    

    【讨论】:

    • 不错的答案,而且它看起来正确,但是当可能有错字或其他东西时,我总是对 UV 有点犹豫。你愿意添加一个演示吗?你可以用我的,测试用例已经修复了。
    猜你喜欢
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    • 2020-12-27
    • 1970-01-01
    相关资源
    最近更新 更多