2017-02-14 67 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; 
}