Search code examples
c++templatesvectorvalarray

How to pass a vector or a valarray as an argument to a C++ template function


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


Solution

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