将int数组转换为可变进值模板

Convert int array to variadic template

本文关键字:int 数组 转换      更新时间:2023-10-16

假设我有一个像int arr[N]这样的int数组,并且假设arr[i]来自一个很小的域(例如1-10)。假设我还有一个可变的模板化类,它有一个公共接口(抽象类)

template <int... A>
class FooImpl : public Foo
{
}

问题是我如何实现一个函数:

Foo* getFoo(int arr[N]);

或者更好:

Foo* getFoo(int* pint, int size);

这将返回FooImpl与模板参数对应我的数组?例如,对于arr = {4,2,6,1},我将得到FooImpl<4,2,6,1>

我找到了问题的答案。诀窍是使用结构变量模板,而不是我最初尝试的函数变量模板。我使用_getFoo_impl结构体和func函数逐个元素构建。

让我们假设元素的范围为[1-5],大小为<= 4,然后代码如下所示:

class Foo
{
};
template <int...A>
class FooImpl : Foo {
};
template<int...As>
struct _getFoo_impl
{
    static Foo* func(int *arr, int sz)
    {
        if (sz == 0)
            return new FooImpl<As...>;
        switch (*arr)
        {
        case 1: return _getFoo_impl<As..., 1>::func(arr + 1, sz - 1);
        case 2: return _getFoo_impl<As..., 2>::func(arr + 1, sz - 1);
        case 3: return _getFoo_impl<As..., 3>::func(arr + 1, sz - 1);
        case 4: return _getFoo_impl<As..., 4>::func(arr + 1, sz - 1);
        case 5: return _getFoo_impl<As..., 5>::func(arr + 1, sz - 1);
        default: throw "element out of range";
        }
    }
};
template<int A1, int A2, int A3, int A4, int A5>
struct _getFoo_impl<A1, A2, A3, A4, A5>
{
    static Foo* func(int*, int sz) {
        std::terminate();
    }
};
Foo* getFoo(int *arr, int size)
{
    return _getFoo_impl<>::func(arr, size);
}

不能从类模板中存储或生成类型

template <int... N>
class FooImpl;

使用运行时整数作为模板参数。但是,如果以下内容也适合您的实际问题,则可以存储并查找指向函数的指针

template <int... N>
R FooFun(A...);

给出运行时整数作为模板参数,假设所有模板参数的函数签名R(A...)相同。注意,A...不是这里的模板参数包;它只是您自己的具体函数参数类型的占位符,例如FooFun(int, int)

在我看来,这个公式确实适合你的问题,因为你有一个没有输入参数的工厂函数,返回一个指向对象FooImpl<N...>的指针,总是被视为Foo*,所以在你的情况下,FooFun看起来像

template <int... N>
Foo* FooFun();

这种转换的一般解决方案是基于指针到函数的查找表,并在我之前的问题的答案中给出,将非constexpr整数值应用于非类型模板参数(顺便说一下,现在工作非常顺利,我对此非常满意-我的实际实现在这里)。

您的情况的不同之处在于您需要一个多维查找表。我建议你首先定义一个函数"dispatcher",只有一个整型模板参数

template <int OFFSET>
R FooDispatch(A...);

表示多维表中的线性偏移量。在这种情况下,前面的解决方案直接应用于FooDispatch。然后,您必须在编译时将OFFSET转换为一组多维索引N...。为此,您还需要在编译时知道表的维度,或者更好的是它的步长。在推导出参数N...之后,FooDispatch现在可以调用FooFun<N...>(...)

更准确地说,这种偏移量到索引转换的逻辑与Matlab的函数ind2sub完全相同,只是它是一个编译时操作。这将需要一些工作,所以如果你喜欢这种方法,并在这最后一步需要帮助,我很乐意协助。在这种情况下,我认为你最好发布一个新的问题,并适当地制定子问题。

以上所有内容都意味着维数在编译时也是已知的,因此最终的形式为

的函数
Foo* getFoo(int arr[N]);

可以作为接口,但是

Foo* getFoo(int* pint, int size);

没有多大意义;您只能在后一种情况下进行运行时检查,如果维度不一致,可能会抛出异常。

最后,请注意,在运行时使用超出范围的索引将产生与在普通C数组中使用超出范围的索引相同的效果。