Search code examples
c++templatesvariadic-templatestemplate-argument-deduction

Deducing number of arguments in a parameter pack


In C++, I'm trying to figure out how one can deduce the size of the parameter pack for a function, and further how one can deduce the number of parameters of a particular type (e.g. ints)

template<int N, int N_ints, typename... Ts> 
void foo(Ts... ts){ 
   ...
}

So that I could call it like


foo(1,2,'3')

and that would be the same as


foo<3,2>(1,2,'3') 

I couldnt even get the basic problem to work, i.e.


template<int N, class... Ts>
std::array<int, N> foo(Ts... ts)
{

    constexpr int n = sizeof...(ts);
    std::array<int, n> out; 
    return out;
}


int main()
{
    auto a = foo(1, 2, 3, '4', 5);
    return 0;
}

fails because it can't deduce N.


Solution

  • You are making this too complicated by adding degrees of freedom that are not actually present. The information on the size of the pack and the number of occurences of int in the pack is already in the pack.

    #include <iostream>
    
    template <typename T,typename... Ts>
    struct count {
        static const unsigned value = (std::is_same_v<T,Ts> + ...);
    };
    
    template<typename... Ts> 
    void foo(Ts... ts){ 
        std::cout << "num params : " << sizeof ...(Ts) << "\n";
        std::cout << "num ints : " << count<int,Ts...>::value << "\n";
    }
    
    int main() {
        foo(1,2,3.0);
    }
    

    I wasn't sure if you want to count all occurences of int in the pack or only the leading ones. I chose the former. Purists may claim that a static_cast<bool> would be more clear, but I think I can get a away with an implicit conversion here.


    If for some reason you need to use the template arguments as in your example, you can still do that:

    template<typename... Ts> 
    auto foo(Ts... ts){ 
        return foo_impl<sizeof ...(Ts),count<int,Ts...>::value>(ts...);
    }