【问题标题】:Choosing std::array template parameters at runtime for limited range of integers在运行时为有限的整数范围选择 std::array 模板参数
【发布时间】:2021-12-21 16:40:31
【问题描述】:

我有一个包含数组的模板类,模板参数就是数组的类型和长度:

template<class T, size_t T2> class Sequence {
    /// Attributes ///
    array <T,T2> sequence;

public:
    /// Methods ///
    Sequence();
    Sequence(const Sequence& s);
    // ... more methods that depend on sizeof(T) and sequence.size() exist here
};

但我需要能够在运行时为有限的size_t k 范围选择 T 和 T2 的组合,例如 4 到 64,以便总位数 (b) 最小比2*kb 的数组中的元素数最小化。

我知道我不能简单地在模板参数中使用一个变量,因为编译器在编译时不知道需要什么,但是应该可以编译一种从整数 k 到几个组合的映射T 和 T2。

映射的外观示例:

k   T         T2
------------------
8   uint8_t   1
9   uint16_t  1
10  uint16_t  1
11  uint16_t  1
12  uint16_t  1
13  uint16_t  1
14  uint16_t  1
15  uint16_t  1
16  uint16_t  1
17  uint8_t   3
18  uint8_t   3
19  uint8_t   3
20  uint8_t   3
21  uint8_t   3
22  uint8_t   3
23  uint8_t   3
24  uint8_t   3
25  uint16_t  2
26  uint16_t  2
27  uint16_t  2
...

此映射的结果必须是可以轻松传递给其他模板类的类/类型,例如根据序列大小对序列进行散列的哈希表。我的问题是,我无法想象在某些时候不涉及使用变量作为模板参数的实现。

为了澄清,我需要做这样的事情,其中​​一个 k 值预计将用于整个程序持续时间,类似于全局常量。

int main(size_t k){
  AbstractSequence<k> s;
  sparse_hash_set <AbstractSequence <k> > k_set;
}

上面的代码是不可能的,因为 k 是一个变量,但我相信编译器应该可以静态断言 k 在可接受的范围内,并编译 k=4 到 k 的必要 T/T2 组合=64 ...

【问题讨论】:

  • 混合类型有什么好处?在实践中,你会为&lt;uint16_t, 1&gt; 做与&lt;uint8_t, 2&gt; 不同的事情吗?
  • 否,但我想使用最大的数据类型,以防止与迭代小字长相关的开销。
  • 因此您确信迭代 3 个 uint8_t 将始终比 2 个 uint16_t 快,但您不想将其扩展到 uint32_tuint64_t 以获得更高位计数。而且我们不知道您在此迭代中在做什么,或者您如何处理未使用的尾随位。我没有看到将类型T 更改为storage 的任何实际价值,仅可能用于iteration。您真的需要将两者混为一谈吗?
  • 表是删减的,我只是没把全系列都写出来...字长次要的要求是数组的总位数不能大大超过序列的长度,k
  • 我的问题更多是关于如何绕过模板的静态限制。

标签: c++ arrays templates template-meta-programming


【解决方案1】:

这就是我会做的:

template<typename T, size_t T2>
struct Sequence {
    Sequence(const std::vector<bool>& v);
};

template<typename Seq>
void f(Seq sequence);

void dynamic_dispatch(const std::vector<bool>& v) {
    if (v.size() <= 8) {
        f(Sequence<uint8_t, 1>(v));
    } else if (v.size() <= 16) {
        f(Sequence<uint16_t, 1>(v));
    } else if (v.size() <= 24) {
        f(Sequence<uint8_t, 3>(v));
    } else if (v.size() <= 32) {
        f(Sequence<uint16_t, 2>(v));
    } else {
        throw std::runtime_error("Unexpected vector size");
    }
}

但是,您也可以进行递归元编程。它是这样的:

template<size_t k_bound>
void dynamic_dispatch_helper(const std::vector<bool>& v) {
    if (v.size() > 8 * k_bound) {
        throw std::runtime_error("Unexpected vector size");
    }
    if (v.size() > 8 * (k_bound - 1)) {  // Recursion stop condition
        f(Sequence<uint8_t, k_bound>(v));  // Simplified T, T2 - can be adapted.
    } else {
        dynamic_dispatch_helper<k_bound - 1>(v); // Recursion!
    }
}

void dynamic_dispatch(const std::vector<bool>& v) {
    dynamic_dispatch_helper<8>(v);
}

我认为这对人类来说很可怕,而且对于编译器的内联逻辑可能不太好,所以我不会。

【讨论】:

  • 您好,感谢您的回答,您能否澄清一下 vector 在您的示例中的作用是什么?由于我认为我的问题可能对要求不够准确,因此我也对其进行了更新。
猜你喜欢
  • 1970-01-01
  • 2014-03-11
  • 2019-12-12
  • 1970-01-01
  • 1970-01-01
  • 2015-05-19
  • 2010-10-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多