0

I know how to use variadic templates and ellipses to accept a variable number of arguments, but how do you pass a variable number of arguments into a function?

Take the following code for example:

#include <iostream>

struct A {
   A(int a, int b) : x(a), y(b) {}
   int x, y;
};

struct B {
    B(int a, int b, int c) : x(a), y(b), z(c) {}
    int x, y, z;
};

template<typename T, typename... TArgs>
T* createElement(TArgs&&... MArgs) {
    T* element = new T(std::forward<TArgs>(MArgs)...);
    return element;
}

int main() {

    int Aargs[] = { 1, 2 };
    int Bargs[] = { 1, 2, 3 };

    A* a = createElement<A>(Aargs); //ERROR
    B* b = createElement<B>(Bargs); //ERROR

    std::cout << "a.x: " << a->x << "\na.y: " << a->y << "\n" << std::endl;
    std::cout << "b.x: " << b->x << "\nb.y: " << b->y << "\nb.z: " << b->z << "\n" << std::endl;

    delete a;
    delete b;
}

Is there any way to expand the arrays so that each of their values is like an argument being passed to the function (similar to parameter pack expansion)?

Or, if not, is there any other way to make this work?

2
  • What's wrong with createElement<A>(1, 2) and createElement<B>(1, 2, 3)? Commented Feb 13, 2022 at 3:16
  • Coupled with its follow-up question, stackoverflow.com/questions/71167015/… this feels like an XY problem. Commented Feb 27, 2022 at 16:28

1 Answer 1

2

You can expand the array using a std::index_sequence

#include <iostream>
#include <utility>

struct A {
   A(int a, int b) : x(a), y(b) {}
   int x, y;
};

struct B {
    B(int a, int b, int c) : x(a), y(b), z(c) {}
    int x, y, z;
};

template<typename T, typename... TArgs>
T* createElement(TArgs&&... MArgs) {
    T* element = new T(std::forward<TArgs>(MArgs)...);
    return element;
}

template<typename T, typename U, size_t...  I>
T* createElementFromArrayHelper(std::index_sequence<I...>, U* a){
    return createElement<T>(a[I]...);
}

template<typename T, typename U, size_t N>
T* createElementFromArray(U (&a)[N]){
    return createElementFromArrayHelper<T>(std::make_index_sequence<N>{}, a);
}

int main() {

    int Aargs[] = { 1, 2 };
    int Bargs[] = { 1, 2, 3 };

    A* a = createElementFromArray<A>(Aargs); 
    B* b = createElementFromArray<B>(Bargs);

    std::cout << "a.x: " << a->x << "\na.y: " << a->y << "\n" << std::endl;
    std::cout << "b.x: " << b->x << "\nb.y: " << b->y << "\nb.z: " << b->z << "\n" << std::endl;

    delete a;
    delete b;

}
Sign up to request clarification or add additional context in comments.

2 Comments

This works but I don't understand how. How is the compiler able to figure out N?
@GaryFisher: It works because the type of Aargs is int[2] and Bargs is int[3]. If the compiler cannot deduce the size at compile time for whatever reason, you can always accept a pointer U* a, but you would still need to specify the size_t N as a template parameter to use std::make_index_sequence<N>{}.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.