c++ - 在编译时自动生成用于稀疏数组索引的 switch 语句

标签 c++ templates c++17 sparse-matrix index-sequence

有没有办法生成编译时 switch 语句来匹配索引?例如,如果我有一个序列 1,5,8 并且我想将它与 0,1,2 匹配,编译器是否可以生成一个函数编译时间,当给出 1、5、8 时分别返回 0、1、2。为了更好地说明我想要什么,我准备了这个稀疏数组结构:https://godbolt.org/z/QpWVST

#include <tuple>
#include <limits>

template<size_t depth, size_t Idx, size_t I, size_t...Is>
size_t get_idx()
{
    static_assert(Idx==I || sizeof...(Is)>0, "Index not found.");
    if constexpr(Idx==I) return depth;
    else return get_idx<depth+1, Idx,Is...>();
}

template<typename T, size_t... Is>
struct sparse_array
{
    constexpr static size_t N = sizeof...(Is);
    constexpr static size_t size() { return N; }

    T e[N];

    constexpr sparse_array() : e{} {}
    template<typename...type_pack>
    constexpr sparse_array(const type_pack&... pack) : e{ pack... }
    {
        static_assert(sizeof...(type_pack) == size(), "Argument count must mach array size.");
    }

    template<size_t I>
    constexpr size_t idx()
    {
        return get_idx<0, I, Is...>();
    }

    size_t idx(const size_t i)
    {
        // how to achieve somethig like this?
        return idx<i>();
    }

    constexpr T& at(size_t idx)
    {
        return e[idx];
    }
};

template<typename T, size_t...Is>
auto make_dense_array(std::index_sequence<Is...>&& seq)
{
    return sparse_array<T, Is...>{};
}

template<typename T, size_t N>
using dense_array = decltype(make_dense_array<T>(std::declval<std::make_index_sequence<N>>()));

size_t test()
{
    dense_array<int, 3> a;
    sparse_array<int,1,5,8> b;
    return b.idx<8>();
}

我还希望能够传入运行时变量,这些变量将通过索引进行切换并返回适当的对应索引。我解决这个问题的唯一想法是生成一个 Is... 序列数组,然后使用带有 if 语句的 for 循环来返回正确的索引。另一种选择是使用 map (但这也不是编译时)。 sparse_array 通常会非常小,我希望能够在编译时完成大部分操作。

最佳答案

是这样的吗?

static constexpr size_t idx(size_t i)
{
    size_t j = 0;
    if(!(... || (j++, i == Is))) {
        throw "Index out of range!";
    }
    return j-1;
}

阅读起来可能有点棘手,但如果我理解正确的话,应该可以做你想做的。实例化后,这基本上等同于一系列 if else 从左到右通过 Is 中的索引。

您可以通过将折叠表达式的主体分隔为 lambda 来使其更具可读性。您还应该将 throw 表达式替换为您认为合适的任何内容。

使用 constexpr 限定符,这也可以用于模板版本:

template<size_t I, auto s = idx(I)>
static constexpr size_t idx() {
    return s;
}

(将结果放在模板默认参数中保证编译时评估。)

这不是性能最好的代码,与手动编写的 switch 语句存在相同的问题。根据输入的可预测性,许多分支可能经常被错误预测,在这种情况下,(大部分)无分支版本会更可取。这可以通过适当修改折叠表达式来实现。

如果索引的数量不小,通过正确构造的静态数组的循环对于指令缓存局部性将是更可取的:

static constexpr size_t idx(size_t i)
{
    static constexpr std::array ind{Is...};
    size_t j = 0;
    for(; j < ind.size() && i != ind[j]; j++);
    if(j == ind.size())
        throw "Index out of range!";
    return j;
}

同样,替换循环中的早期退出可能更可取。

如果数组更大,不仅使用带循环的声明数组,而且正确地对该数组实现二进制搜索可能会很有用。

std::any_ofstd::findstd::binary_search 等标准算法可以代替手动搜索实现.然而,这些算法将仅在 C++20 中是constexpr,因此这会限制此处的使用。

关于c++ - 在编译时自动生成用于稀疏数组索引的 switch 语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58531898/

相关文章:

C++11 初始化列表、数组和枚举的乐趣

c++ - bool类型的变量是如何存储在内存中的? (C++)

c++ - shared_ptr 删除器问题

c++ - 为什么只能在头文件中实现模板?

c++ - 在构造函数的可变参数中使用其他模板化类执行模板化类的初始化

c++ - OpenCV imshow 不在 osx 中显示图像

c++ - 按升序合并两个数组(两个数组大小相同)

c++ - 指向模板对象的指针

c++ - 如何编写通用特征以将 T::value_type 与其他特征相匹配?

visual-studio - Visual Studio 2019 中缺少 std::aligned_alloc() 吗?