【问题标题】:Convert int array to variadic template将 int 数组转换为可变参数模板
【发布时间】:2014-04-30 15:36:19
【问题描述】:

假设我有一个像 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&lt;4,2,6,1&gt;

【问题讨论】:

  • 如果您想知道如何在运行时提供模板参数(一个编译时的东西)返回FooImpl,你不能。您可以为两者使用类似的初始化程序,但运行时数据评估不能产生编译时实现。
  • 我很确定我可以。在最坏的情况下,我可以简单地枚举所有大小 = 1、2 等的案例......大小 = 1 有 10 个案例,大小 = 2 有 100 个案例等等。请注意,所有这些类都具有相同的基类(即接口)Foo,并注意我的元素来自有限的集合。
  • 我真的不知道你刚才说了什么,但我坚持我的意见。您无法运行时内容推导出编译时模板参数。如果您认为您可以执行 int ar[3]; ar[0] = 1; ar[1] = 2; ar[2] = 3; 之类的操作,那么以某种方式将 ar 发送到工厂,这将导致 FooImpl 我看到它发生的唯一方法是一个包含 1000 个条目的静态表,每个插槽静态定义为返回特定的FooImpl&lt;a,b,c&gt; 但此时它并不是真正的运行时。你肯定不会继承它。
  • 好的,我就是这么说的。我可以通过硬编码我需要的所有案例来做到这一点。直到 size = 3 我将有 1000+100+10=1110 种不同的情况,并且有那么多行代码来涵盖所有这些情况。我的问题是如何以聪明的方式使我的代码不超过 1110 行代码?
  • 好吧,至少看起来我当时8确实理解你。我必须考虑一下,看看是否可以使用模板生成器模板。

标签: c++ arrays int variadic-templates non-type


【解决方案1】:

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

假设元素在 [1-5] 范围内且大小

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);
}

【讨论】:

  • 如果你有 100 个元素呢?
  • 然后您生成代码,但检查问题,它明确表示“小域”。
【解决方案2】:

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

template <int... N>
class FooImpl;

使用 run-time 整数作为模板参数。但是,如果以下内容也适合您的实际问题,您可以存储并查找 pointer-to-function

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

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

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

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

这种转换的一般解决方案是基于函数指针的查找表,并在我之前的问题adapting a non-constexpr integral value to a non-type template parameterthis answer 中给出(顺便说一下,现在工作非常顺利,我我对此非常满意 - 我的实际实现是 here)。

你的情况不同的是你需要一个多维查找表。我建议你首先定义一个只有一个整数模板参数的函数“dispatcher”

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

表示多维表中的线性偏移量。在这种情况下,前面的解决方案直接适用于FooDispatch。然后,您必须在编译时将OFFSET 转换为一组多维索引N...。为此,您还需要在编译时再次了解表格的尺寸,或者更好地了解它的步幅。推导出参数N...FooDispatch 现在可以调用FooFun&lt;N...&gt;(...)

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

以上所有暗示维数在编译时也是已知的,所以最终是形式的函数

Foo* getFoo(int arr[N]);

作为一个界面会很好,但是

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

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

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

【讨论】:

  • 我想我不太明白你的回答。老实说,我期待一段代码,即函数 getFoo() 的实现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-11
  • 2021-11-01
  • 1970-01-01
相关资源
最近更新 更多