Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

function template: default first template argument to second

Is is possible to make the first template argument of a function template default to the second one if the first one is not specified?

Here is a small example:

#include <algorithm>
#include <list>
#include <vector>

template <typename ContainerOut, typename ContainerIn>
ContainerOut KeepNegatives(const ContainerIn& xs)
{
    ContainerOut result;
    auto itOut = std::inserter(result, std::end(result));
    auto isNegative = [](auto x){ return x < 0; };
    std::copy_if(std::begin(xs), std::end(xs), itOut, isNegative);
    return result;
}

int main()
{
    typedef std::vector<int> IntVector;
    typedef std::list<int> IntList;

    IntVector intVector = { 1, -2, -3, 4 };
    IntList intList = { 1, -2, -3, 4 };

    auto intVec2 = KeepNegatives<IntVector>(intList);
    auto intList2 = KeepNegatives<IntList>(intVector);
    auto intVec3 = KeepNegatives<IntVector>(intVector);
}

This works, but what I want is, that the type of the return value of KeepNegatives (i.e. ContainerOut) is the same as the type of the input value (i.e. ContainerIn) in case ContainerOut is not specified. So that the following line of code would compile (right now it does not) and return an IntVector.

    auto intVec4 = KeepNegatives(intVector);
like image 360
Tobias Hermann Avatar asked Jan 07 '23 03:01

Tobias Hermann


1 Answers

You could simply add an overload for this special case:

template <typename ContainerIn>
ContainerIn KeepNegatives(const ContainerIn& xs)
{
    return KeepNegatives<ContainerIn, ContainerIn>(xs);
}

This, however, can cause an ambiguity in your intVec3 case. Here's one way around it:

#include <algorithm>
#include <list>
#include <vector>

template <typename ContainerOut, typename ContainerIn, 
          typename = std::enable_if_t<!std::is_same<ContainerOut, ContainerIn>::value>>
ContainerOut KeepNegatives(const ContainerIn& xs)
{
    ContainerOut result;
    auto itOut = std::inserter(result, std::end(result));
    auto isNegative = [](auto x){ return x < 0; };
    std::copy_if(std::begin(xs), std::end(xs), itOut, isNegative);
    return result;
}

template <typename ContainerIn>
ContainerIn KeepNegatives(const ContainerIn& xs)
{
    return KeepNegatives<ContainerIn, ContainerIn, void>(xs);
}


int main()
{
    typedef std::vector<int> IntVector;
    typedef std::list<int> IntList;

    IntVector intVector = { 1, -2, -3, 4 };
    IntList intList = { 1, -2, -3, 4 };

    auto intVec2 = KeepNegatives<IntVector>(intList);
    auto intList2 = KeepNegatives<IntList>(intVector);
    auto intVec3 = KeepNegatives<IntVector>(intVector);
    auto intVec4 = KeepNegatives(intVector);
}

Live

like image 70
Rostislav Avatar answered Jan 14 '23 21:01

Rostislav