【问题标题】:Reverse order of varidic template arguments while constructing a map key构造映射键时可变参数模板参数的逆序
【发布时间】:2021-11-26 19:42:52
【问题描述】:

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

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?我已经看到了反转可变参数的解决方案,它们似乎过于复杂。

【问题讨论】:

  • 在标准 C++ 中没有简单的方法来反转参数的顺序。您可能会在库中找到一些助手,也许 Boost::Hana 有一些。在您的情况下,我建议将a * p^2 + b * p + c 重写为(a * p + b) * p + c,这样您就不需要颠倒参数的顺序。
  • 也许有一些简单的方法可以滥用std::tuple 或其他容器,但我没有找到解决方案:/
  • 只要有一个元组,它就会和你的一样hacky ;)
  • @hochl 你可以先make_tuple 或者forward_as_tuple 然后std::apply,但是你还是要反过来std::tuple,没有简单的办法。
  • 您可以尝试使用sizeof...(Args) 预先计算给定迭代的基数。

标签: c++ templates variadic-templates


【解决方案1】:

从 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

【讨论】:

  • 我改用这个解决方案,所以我会接受那个。
【解决方案2】:

根据@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);
}

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      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;写一个做整数幂运算的函数。

      【讨论】:

        猜你喜欢
        • 2023-03-24
        • 2013-10-09
        • 1970-01-01
        • 2014-04-21
        • 2018-05-18
        • 2015-05-06
        • 1970-01-01
        • 2016-09-02
        • 2019-01-29
        相关资源
        最近更新 更多