将可变参数模板转换为 constexpr

Convert variadic template to constexpr

我想知道是否可以使用更简单的代码将通过参数包调用的简单循环转换为 constexpr。此示例代码演示了我正在尝试做的事情

struct Student {
    AgeCategory age;
    Income income; 
    bool is_student;
    CreditRating credit_rating;
    bool buys_computer;
};  

auto find_probability(const double x, const double mean, const double stdev) -> double;

typedef std::tuple<double, double> MeanStdDev;
typedef std::vector<MeanStdDev> MeanStdDevVec;


// This code seems verbose to me. Is there a simpler way to express this
// loop which iterates over a vector and parameter pack, possibly 
// using constexpr. C++14/17 idioms are fine.
template<typename Attr>
auto get_probability(const MeanStdDevVec& v, const size_t n, const Student& s, Attr attr) -> double {
    double mean, stdev;
    std::tie(mean, stdev) = v[n];

    return find_probability(static_cast<double>(std::invoke(attr, s)), mean, stdev);
}

template<typename First, typename... Attr>
auto get_probability(const MeanStdDevVec& v, const size_t n, const Student& s, First f, Attr... attr) -> double {
    double mean, stdev;
    std::tie(mean, stdev) = v[n];

    return find_probability(static_cast<double>(std::invoke(f,s)), mean, stdev) * get_probability(v, n + 1, s, attr...);
}

template<typename ...Attr>
auto calculate_class_probability(const std::map<bool, MeanStdDevVec>& summaries, const Student& s, Attr... attributes) {
    for (const auto& i : summaries) {
        get_probability(i.second, 0L, s, attributes...);
    }
}

来自

 Student s;
 calculate_class_probability(class_summaries, s , &Student::age, &Student::income, &Student::credit_rating, &Student::is_student);

它不一定使代码整体更短,但它确实分离出一个通用部分,您可以轻松地重复使用,并且恕我直言,使代码更清晰。这种特殊情况下的关键是将包映射到特定类型数组的函数:

template <class T, class F, class ... Args>
std::array<T, sizeof...(Args)> pack_to_array(F f, Args&& ... args) {
    return {(f(std::forward<Args>(args)))...};
}

在你的情况下,这还不够,因为你想用矢量压缩它。因此,对此的一个有用的修改是使 pack 元素的整数索引可用并将其传递给函数:

template <class T, class F, class ... Args>
std::array<T, sizeof...(Args)> index_pack_to_array(F f, Args&& ... args) {
    std::size_t i = 0;
    return {(f(i++, std::forward<Args>(args)))...};
}

现在,您可以像这样使用此功能:

template<typename... Attr>
double get_probability(const MeanStdDevVec& v, const Student& s, Attr... attr) {

    assert(v.size() == sizeof...(Attr));
    auto probs = index_pack_to_array<double>(
        [&] (std::size_t i, auto&& a) -> double { 
            return // ... (get probability from v[i], s, and a)
        },
        std::forward<Attr>(attr)...);

    return std::accumulate(probs.begin(), probs.end(), 1.0,
        [] (double p1, double p2) { return p1 * p2; });
}

不确定你想要什么,但我想你可以扔掉你的 getProbability() 并重写 calculate_class_probability() 如下

template <typename ... Attr>
auto calculate_class_probability
     (std::map<bool, MeanStdDevVec> const & summaries,
      Student const & s, Attr ... attributes)
 {
   using unusedT = int[];

   for ( const auto & i : summaries )
    {
      double       mean, stdev;
      double       prob {1.0};
      std::size_t  n {0};

      (void)unusedT { (std::tie(mean, stdev) = i.second[n++],
                       prob *= find_probability(static_cast<double>(std::invoke(attributes,s)), mean, stdev),
                       0)... };

      // in prob the calculate value
    }
 }

但是:不:我不认为可以把它写成constexprstd::vector<>operator[] 不是 constexpr