构造映射键时可变参数模板参数的逆序

Reverse order of varidic template arguments while constructing a map key

我正在使用可变参数模板为地图构造键,计算基数的数字:

template<typename T>
uint64_t key(int base, T n)
{
    return uint64_t(n) % base;
}

template<typename T, typename... Args>
uint64_t key(int base, T n, Args... rest)
{
    return key(base, rest...) * base + (uint64_t(n) % base);
}

key(10, 1, 2, 3) 调用它给我一个十进制值 321 的键。我更愿意为密钥获取 123,并且我找到了一个有效的解决方案:

template<typename T>
uint64_t keyHelper(int& mul, int base, T n)
{
    mul = base;
    return uint64_t(n) % base;
}

template<typename T, typename... Args>
uint64_t keyHelper(int& mul, int base, T n, Args... rest)
{
    int mul_tmp;
    uint64_t result = keyHelper(mul_tmp, base, rest...) +
        (uint64_t(n) % base) * mul_tmp;
    mul = mul_tmp * base;
    return result;
}

template<typename... Args>
uint64_t key(int base, Args... args)
{
    int mul;
    return keyHelper(mul, base, args...);
}

不过,这个解决方案感觉像是一个 hack,因为它绕过一个引用来修复乘法的指数。是否有一种简单的可变方法,模板可以按所需顺序计算数字,即 123?我见过反转可变参数的解决方案,但它们似乎过于复杂。

怎么样this:

template<typename T>
uint64_t key_impl(int base, unsigned int exp, T n)
{
    return uint64_t(n) % base;
}

template<typename T, typename... Args>
uint64_t key_impl(int base, unsigned int exp, T n, Args... rest)
{
    uint64_t res = uint64_t(n) % base;
    for (unsigned int i = 0u; i < exp; ++i)
        res *= base;
    return key_impl(base, exp - 1u, rest...) + res;
}

template<typename... Args>
uint64_t key(int base, Args... args)
{
    return key_impl(base, sizeof...(Args) - 1u, args...);
}

由于参数个数已知,最大指数已知,你可以直接用它来计算总和

template<typename T, typename... Args>
uint64_t key(int base, T n, Args... rest)
{
    return (uint64_t(n) % base) * std::pow(base, sizeof...(rest))
            + key(base, rest...);
}

(基本情况不变)

demo

请注意,您实际上不应该在这里使用 std::pow;编写一个执行整数幂运算的函数。

从 C++17 开始,我会使用折叠表达式 (op,...) 来做到这一点:

template<class B, class ... Args>
auto key(B base, Args ... args) {
    std::common_type_t<Args...> res{};
    ( (res *= base, res += args % base), ... );
    return res;
}

Demo

根据@Andrey Semashev 的评论,我找到了一个感觉更好的解决方案:

template<int n>
uint64_t exp(int base)
{
    return base * exp<n - 1>(base);
}

template<>
uint64_t exp<0>(int base)
{
    return 1;
}

template<typename T>
uint64_t key(int base, T n)
{
    return uint64_t(n) % base;
}

template<typename T, typename... Args>
uint64_t key(int base, T n, Args... rest)
{
    return key(base, rest...) +
        (uint64_t(n) % base) * exp< sizeof...(rest) >(base);
}