c++ - Multiple parameter packs for variadic struct

78 Views Asked by At

This question is similar to this C++ - Multiple parameter packs for variadic function, but specifically for structs.

The following compiles and runs fine (taken from the linked question).

template <typename... TDependencies, typename ...TArgs>
void fooFunction(TArgs...args) {
    std::cout << "TDependecies list:" << std::endl;
   ((std::cout << "- " << typeid(TDependencies).name() << std::endl), ...);

    std::cout << "TArgs list:" << std::endl;
   ((std::cout << "- " << typeid(TArgs).name() << std::endl), ...);
}

int main()
{
    fooFunction<std::string, short, int, long, long long>(0, 1l, 2ll);
}

as the type deduction will be successful based on the arguments provided to fooFunction.

My question is, why does the deduction not work with a struct like this?


template <typename... TDependencies, typename ...TArgs>
struct fooStruct {
    fooStruct(TArgs...args) {
        std::cout << "TDependecies list:" << std::endl;
        ((std::cout << "- " << typeid(TDependencies).name() << std::endl), ...);

        std::cout << "TArgs list:" << std::endl;
        ((std::cout << "- " << typeid(TArgs).name() << std::endl), ...);
    }
};

int main()
{
    fooStruct<std::string, short, int, long, long long> s(0, 1l, 2ll);
}

The compiler error is parameter pack ‘TDependencies’ must be at the end of the template parameter list.

I've tried adding a user deduction guide like below, but that gives the error trailing return type ‘<type error>’ of deduction guide is not a specialization of ‘fooStruct<<declaration error>, TArgs>’

template <typename... TDependencies, typename ...TArgs>
fooStruct(const TArgs...) -> fooStruct<TDependencies..., TArgs...>;

is there any way to accomplish this?

For context the ultimate goal is to have something like this to zip the two variadic packs together.

template <typename T1, typename T2>
struct base {

};

template <typename... T1, typename ...T2>
struct child : base<T1, T2>... {

    child(T2... args) {}
};

child here will use T2 but base will use T1 and T2.

2

There are 2 best solutions below

0
Yakk - Adam Nevraumont On

The error is clear: the standard forbids it.

To solve your problem you need a factory function. And you know how to write that factory function.

Partial CTAD is banned by the standard. It was complex and not clear so they nixed it. So there is no way to deduce half of the arguments, other than a factory function.

template<class...Explicit, class...Implicit>
foo<pack<Explicit...>, pack<Implicit...>>
 make_foo(Implicit...);

using

template<class...>struct pack{};

to delimit arguments into two packs.

1
user12002570 On

is there any way to accomplish this?

Yes, the simplest way is to make the constructor itself a template as shown below:

template <typename... TDependencies> //removed TArgs from here
struct fooStruct {
    template<typename ...TArgs> //added this 
    fooStruct(TArgs...args) {
        std::cout << "TDependecies list:" << std::endl;
        ((std::cout << "- " << typeid(TDependencies).name() << std::endl), ...);

        std::cout << "TArgs list:" << std::endl;
        ((std::cout << "- " << typeid(TArgs).name() << std::endl), ...);
    }
};

int main()
{
    fooStruct<std::string, short, int, long, long long> s(0, 1l, 2ll); //works now !!
}