Home > Back-end >  Is there a way to pass a brace enclosed list to variadic templates?
Is there a way to pass a brace enclosed list to variadic templates?

Time:10-27

I would like to pass a variable list of objects to be passed to a template function as a series of brace enclosed initializers.

So something like this:

enum class E { a, b, c };

template <typename T>
struct Info
{
    template <typename U>
    Info(E e, U u)
        : e(e)
        , size(sizeof(u))
    {}

    E e;
    size_t size;
};

template <typename...Ts>
void fn(Info<Ts>&&...args) { }

int f() {
    fn({ E::a, 5 });
}

If I replace the above with concrete types, the brace enclosed initializers does work:

enum class E { a, b, c };

struct InfoBase { E e; int size; };

void f1(InfoBase&& a, InfoBase&& b) { }

void f() {
    f1({ E::a, 4 }, { E::b, 6 });
}

But replacing f1 with the following fails:

void f1() { }

template <typename...Ts>
void f1(InfoBase&& a, Ts&&...args)
{
    f1(std::forward<Ts>(args)...);
}

Presumably because it's can't infer Ts?

Error messages are:

<source>: In function 'void f()':
<source>:22:7: error: no matching function for call to 'f1(<brace-enclosed initializer list>, <brace-enclosed initializer list>)'
   22 |     f1( { E::a, 4 }, { E::b, 6 } );
      |     ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:16:6: note: candidate: 'void f1(InfoBase&&, Ts&& ...) [with Ts = {}]'
   16 | void f1(InfoBase&& a, Ts&&...args) {
      |      ^~
<source>:16:6: note:   candidate expects 1 argument, 2 provided
<source>:14:6: note: candidate: 'void f1()'
   14 | void f1() { }
      |      ^~
<source>:14:6: note:   candidate expects 0 arguments, 2 provided

I'm thinking that because Ts has no concreate type to bind to it just fails, even though that information is available later in the call tree. Is that correct?

Is what I'm attempting just not possible?

CodePudding user response:

Since the plain {}, has no type, it can not be used in the template type deduction.

From what I understood, that you're trying to achieve, I propose the following

template <typename T, typename...Ts>
void fn(E e, T&& val, Ts&&...args)
{
    Info<T> ob{ e, std::forward<T>(val) };  // do something with ob
    // ....

    if constexpr (sizeof...(args) == 1u) {
        std::cout << "At least required two args!\n";
        // other error msgs or handling
    }
    else if constexpr (sizeof...(args)) {   
        fn(std::forward<Ts>(args)...); // process what left by recursive calls
    }
}

Now pass/ call the function as

fn(E::a, 4, E::b, 6);

See a demo in godbolt.org

  • Related