Reputation: 878
On C++ weekly ep. 48 by Jason Turner there is the following code:
template<typename ... B>
struct Merged : B...
{
template <typename ... T>
Merged(T && ... t) : B(std::forward<T>(t))...
{ }
using B::operator()...;
};
It fails with the following on gcc 7.1:
error: mismatched argument pack lengths while expanding 'B'
What is the proper way to Expand B?
(BTW, in the above link, the code seems to compile with some 7.0 snapshot).
Edit1:
As noted by @Jarod42, it will compile with Structs acting as the functors. The actual video use lambda and it seems to break there.
auto l1 = [] { return 4 ; };
auto l2 = [](const int i) { return i * 10; };
// This would work, S1, S2 are just functors structs
Merged<S1, S2> merged1(42, "hello");
// This fails
Merged merged2 = Merged(l1, l2);
Edit2:
Seems like User-defined deduction guides does not work here.
template <typename ... T>
Merged(T...) -> Merged<std::decay_t<T>...>;
The above should have enable the following:
Merged merged(l1, l2);
But it does not. It seems like you have to pass the types to Merged<>
Merged<t1, t2> merged(l1, l2);
which probably not really what the tutorial wanted to demonstrate.
Upvotes: 2
Views: 241
Reputation: 368
If the sizes of the parameter packs should be equal, we can enforce this with std::enable_if
:
template<typename ... B>
struct Merged : B...
{
template <typename ... T,
typename = typename std::enable_if_t<sizeof...(T) == sizeof...(B)>>
Merged(T && ... t) : B(std::forward<T>(t))...
{ }
using B::operator()...;
};
This together with the user-defined deduction guide seems to work.
Upvotes: 2
Reputation: 878
Seems like there is no need to have any constructor defined here, and instead, just using C++17 aggregate:
template<typename ... B>
struct Merged : B...
{
// This is not needed, it would actually render this class
// to be a non-aggregate one.
// template <typename ... T>
// Merged(T && ... t) : B(std::forward<T>(t))...
// { }
using B::operator()...;
};
// C++17 class deduction guidance (user defined)
template <typename ... T>
Merged(T...) -> Merged<T...>;
int main()
{
auto l1 = [] { return 4 ; };
auto l2 = [](const int i) { return i * 10; };
// Note here, using {} for initializing an aggregate
Merged merged{l1, l2};
}
Note the C++17 class deduction user defined guidance, plus using {} for initializing an aggregate.
Constructor is not needed. If we had it, this class would not be an aggregate anymore and the syntax used for initializing, would stop working.
Upvotes: 2
Reputation: 217065
With
template <typename ... T>
Merged(T&& ... t) : B(std::forward<T>(t))...
{}
sizeof...(T)
should be equal to sizeof...(B)
:
You have to provide one argument by base.
And then it works.
If you don't provide same number of argument, you hace error similar to:
error: mismatched argument pack lengths while expanding 'B'
Upvotes: 1