2017-02-16 21 views
1

我想知道是否可以通過參數包將簡單循環轉換爲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); 

回答

1

呼籲並不一定使代碼更短,作爲一個整體,但它並分離出一個通用的一部分,你可以輕鬆重用,恕我直言,使代碼更清晰。在這種特殊情況下,關鍵是映射包成某種類型的數組功能:

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

在你的情況,這是不是很夠,只要你想用向量來壓縮它。所以這是一個有用的修改,將是使包裝元素的整數索引可用,並將它傳遞給函數:

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; }); 
} 
+0

這很聰明,所以謝謝 – Ronnie

+0

@Ronnie謝謝!如果您覺得它回答您的問題,請將其標記爲已接受(打勾)。乾杯。 –

0

不是一定要了解你想要什麼,但我想你可以扔掉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 
    } 
} 

不過:不,我不認爲這是可能的WR將此作爲constexpr; operator[]對於std::vector<>不是constexpr

相關問題