Search code examples
c++templatesvariadic-templates

Understanding C++ variable number of input parameters to test_function(Test<T>...)


I have a generic class Test<T> and I want to have a function test_function() that has a variable number of Test<T> object input parameters with .... In the function, I want to iterate over all the parameters. The generic type T can be different across the parameters. Something like this:

template <typename T> class Test {
private:
    T value = (T)0;
    int test = 1;
public:
    Test() = default;
    int get_test() {
        return test;
    }
}

template <typename T> void test_function(const Test<T> tests...) {
    for(auto test : tests) {
        cout << test.get_test() << endl;
    }
}

When compiling, I get the errors:

error C3520: "T": Parameter pack must be extended in this context
error C3520: "tests": Parameter pack must be extended in this context
error C3312: no callable 'begin' function found for type 'unknown-type'
error C3312: no callable 'end' function found for type 'unknown-type'

What am I doing wrong?

EDIT: Is it possible to have a counter in the expansion?

EDIT 2: I figured it out with the counter:

template <typename ...T> void test_function(const Test<T> ...tests) {
    int i=0;
    ((cout << tests.get_test() << ", counter = " << i++ << endl), ...);
}

Solution

  • There are multiple problems here.

    First, the correct variadic template function declaration should be:

    template <typename ...T> void test_function(const Test<T> ...tests)
    

    But that won't solve all the problems. The first one is that all the parameters are const objects, therefore the class method must also be a const class member:

    int get_test() const {
    

    Finally:

    for(auto test : tests) {
    

    tests is not a container that can be range-iterated over, like a vector. It is a parameter pack and needs to be expanded like one:

    ((cout << tests.get_test()), ...);
    

    Tested with gcc 11:

    #include <iostream>
    
    using namespace std;
    
    template <typename T> class Test {
    private:
        T value = (T)0;
        int test = 1;
    public:
        Test() = default;
        int get_test() const {
            return test;
        }
    };
    
    template <typename ...T> void test_function(const Test<T> ...tests) {
    
        ((cout << tests.get_test()), ...);
    }
    
    void foo()
    {
        test_function(Test<int>{}, Test<char>{});
    }