2017-02-14 71 views
1

有了這個小例子,我試圖讓編譯器自動推導第二個參數的模板參數。這有效,但並不像我想的那麼簡潔。std ::函數模板參數扣除成員屬性

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


// This works (A) 
template<typename R> 
auto calc_mean(const std::vector<Student> & in, std::function<R (Student const&)> attr)-> double 
{ 
    const auto mean = std::accumulate(std::begin(in), std::end(in), 0.0, [&attr](auto acc, const auto& val) { 
     // Call the attribute passed in 
     return acc += static_cast<double>(attr(val)); 
    })/static_cast<double>(in.size()); 
    return mean; 
} 

// This doesn't work (B) 
template<typename T> 
auto calc_mean(const std::vector<Student> & in, T attr)-> double 
{ 
    const auto mean = std::accumulate(std::begin(in), std::end(in), 0.0, [&attr](auto acc, const auto& val) { 
    // Call the attribute passed in 
    return acc += static_cast<double>(attr(val)); 
})/static_cast<double>(in.size()); 
    return mean; 
} 

// Caller (A) - works but I have to explicitly state the attribute type 
mean_stddev<AgeCategory>(buy, &Student::age); 
// Caller (B) - what I'd like to be able to do and let compiler infer types 
mean_stddev(buy, &Student::age); 

錯誤是

>..\src\Main.cpp(16): error C2672: mean_stddev': no matching overloaded function found 
1>..\src\Main.cpp(16): error C2784: 'std::tuple<double,double> mean_stddev(const std::vector<Student,std::allocator<_Ty>> &,T *)': could not deduce template argument for 'T *' from AgeCategory Student::* ' 
1>   with 
1>   [ 
1>    _Ty=Student 
1>   ] 
1> c:\users\chowron\documents\development\projects\ml\src\Bayes.h(25): note: see declaration of mean_stddev' 

什麼我必須做的函數聲明對於B用更簡潔的語法工作。

回答

1

要調用attr,你需要使用std::invoke

template <class R> // <-- NB: R, not T 
double calc_mean(const std::vector<Student>& in, R attr) 
{ 
    const auto mean = std::accumulate(in.begin(), in.end(), 0.0, [&attr](auto acc, const auto& val) { 
     return acc + static_cast<double>(std::invoke(attr, val)); 
    })/static_cast<double>(in.size()); 
    return mean; 
} 

還是老老實實:

template <class R> // <-- NB: R, not T 
double calc_mean(const std::vector<Student>& in, R attr) 
{ 
    double sum = 0.0; 
    for (auto const& s : in) { 
     sum += std::invoke(attr, s); 
    } 
    return sum/in.size(); 
} 

invoke()是一個C++ 17函數模板,但您可以在每個附加參考的C++ 11中實現它。它會爲函數,函數對象和指向成員的指針做正確的事 - 這基本上就是你想要的。

+0

我不知道關於invoke。似乎也在MSVC15中工作。謝謝 – Ronnie

+1

C++ 11 kludge是'std :: ref(attr)(s)'。 –

+0

@ T.C。這讓我很難過。我需要重寫指向成員的提案並重新提交。我真的只想寫'attr(s)'。 – Barry

0

對於B使用更簡潔的語法來使用函數聲明,我該做些什麼。

首先,你應該使用相同的模板識別器:或RT

template<typename T> // <--- use R here 
auto calc_mean(const std::vector<Student> & in, R attr)-> double 
{ 
    const auto mean = std::accumulate(std::begin(in), std::end(in), 0.0, [&attr](auto acc, const auto& val) { 
    // Call the attribute passed in 
    return acc += static_cast<double>(attr(val)); 
})/static_cast<double>(in.size()); 
    return mean; 
}