简体   繁体   中英

std::function template argument deduction for member attributes

With this small example, I'm trying to get the compiler to automatically deduce the template argument for the 2nd parameter. This works but is not as succinct as I would like.

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

Error is

>..\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'

What do I have to do to the function declaration for B to work with the more concise syntax.

To invoke attr , you need to use 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;
}

Or honestly:

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() is a C++17 function template, but you can implement it in C++11 per the reference attached. It will do the right thing for both functions, function objects, and pointers to members - which is basically what you want.

What do I have to do to the function declaration for B to work with the more concise syntax.

First of all, you should use the same template identificator: or R or T

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM