I feel this is probably an elementary question, but I can't find a simple answer after quite a bit of searching, so I thought I'd ask.
I have a function that is meant to return the nth percentile value in a container, but for legacy reasons the array can be either a vector or valarray, and it can contain doubles or floats. What is the correct syntax for the function? At the moment I have:
template <template <class> class vType, class elType>
elType GetPercentile(vType<elType>& vData, double dPercentile)
{
int iOffset = int(dPercentile * vData.size());
std::nth_element(begin(vData), begin(vData) iOffset, end(vData));
return static_cast<elType>(vData[iOffset]);
}
This compiles OK when passing a valarray, but fails for a vector:
'elType GetPercentile(vType &,double)': could not deduce template argument for 'vType &' from 'std::vector<float,std::allocator>'
Is there a way of doing this? It seems silly to duplicate the code for the two container types. (And if there are any comments on the code itself, that would be fine too.)
Many thanks for any advice. Bill H
CodePudding user response:
You are making the template too complicated. Just do this. No need to static cast. Use auto return deduction.
As an aside, you probably don't want to pass vData by reference because std::nth_element
will alter the caller's data in the process of partial sorting. Also dPercentile
is poorly named since you don't scale the fractional value by 100. Consider scaling it or renaming the parameter.
#include <vector>
#include <valarray>
#include <algorithm>
template<class vType>
auto GetPercentile(vType vData, double dPercentile)
{
int iOffset = int(dPercentile * vData.size());
std::nth_element(begin(vData), begin(vData) iOffset, end(vData));
return vData[iOffset];
}
int main()
{
std::vector<float> v{ 1,2,3,4 };
std::valarray<float> va{ 1., 2., 3., 4. };
auto nth_v = GetPercentile(v, .5); //returns 3.f
auto nth_va = GetPercentile(va, .5); //returns 3.f
};
CodePudding user response:
std::vector
has 2 template parameters. The second one is the allocator, which has a default value so you normally don't use it.
However, prior to c 17
template template parameters would only match if the number of template arguments where the same. In c 17 this was relaxed a bit and it's since allowed to match a template with more template parameters as long as the remaining ones have default arguments.
Regardless of this, I would propose a solution that uses the member type in both containers, value_type
.
#include <vector>
#include <algorithm>
#include <valarray>
template <class T>
auto GetPercentile(T& vData, double dPercentile)
{
using elType = typename T::value_type;
int iOffset = int(dPercentile * vData.size());
std::nth_element(begin(vData), begin(vData) iOffset, end(vData));
return static_cast<elType>(vData[iOffset]);
}
int main() {
auto v = std::vector<int>{1,2,3,4,5};
GetPercentile(v, 2);
auto a = std::valarray<int>(5, 5);
GetPercentile(a, 2);
}