Parameter Pack Must Be at the End of the Parameter List... When and Why

Parameter pack must be at the end of the parameter list... When and why?

It is valid for function templates but only when argument deduction can help the compiler resolve the template parameters, as it stands your function template example is virtually useless because

template<typename T, typename... Args, typename S> void fn() { }
int main() { fn<int, int, int>(); }
test.cpp: In function 'int main()':
test.cpp:2:32: error: no matching function for call to 'fn()'
int main() { fn<int, int, int>(); }
^
test.cpp:1:57: note: candidate: template<class T, class ... Args, class S> void fn()
template<typename T, typename... Args, typename S> void fn() { }
^
test.cpp:1:57: note: template argument deduction/substitution failed:
test.cpp:2:32: note: couldn't deduce template parameter 'S'
int main() { fn<int, int, int>(); }

the compiler has no way of determining which template parameters belong to the parameter pack, and which to S. In fact as @T.C. points out it should actually be a syntax error because a function template defined in this manner cannot ever be instantiated.

A more useful function template would be something like

template<typename T, typename... Args, typename S> void fn(S s) { }

as now the compiler is able to unambiguously match the function parameter s with the template type S, with the side effect that S will always be deduced - all explicit template parameters after the first will belong to Args.

None of this works for (primary) class templates, parameters aren't deduced and it's expressly forbidden:

From draft n4567

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf

[temp.param] / 11

[...]If a template-parameter of a primary class template or alias
template is a template parameter pack, it shall be the last
template-parameter.[...]

(if they were deduced it would be ambiguous as in the function template example).

Why must the template parameter pack be last?

The variable template declaration is ill-formed, and it's a gcc bug for not diagnosing it.

From temp.param#14:

... If a template-parameter of a primary class template, primary variable template, or alias template is a template parameter pack, it shall be the last template-parameter. ...

The function template is fine, since the rules for function templates are different. If the template parameters after the parameter pack can be deduced by the arguments provided at the call site, then the template is ok.

Why am I getting the error parameter pack 'F' must be at the end of the template parameter list

You have another parameter, T, at the end of the list after F. As the error message says, the variadic pack must come at the end. Unfortunately, that makes it awkward to have both variadic and defaulted parameters in the same template.

Function template parameter pack not at the end of the parameter list

The interesting part from Clang's error message is:

main.cpp:11:6: note: candidate template ignored: couldn't infer template argument 'T'

void foo(Args... args, T x) {
^

The problem is that the parameter pack Args... occurs prior to T.

Args... is "greedy" and so no parameters are left for the compiler to deduce T, hence it fails.

Quoting the standard (emphasis mine):

[temp.param]/11

A template parameter pack of a function template shall not be followed
by another template parameter unless that template parameter can be
deduced
from the parameter-type-list of the function template or has a
default argument. [Example:

...
// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { } // error
template<class... T, class U> void g() { } // error

— end example]

Error with variadiac template: parameter pack must be expanded

The error is due to the missing ellipsis (...) after args when passing all individual parameters (rather than the parameter pack) to emplace_back.

The fixed (and improved) version:

template<class Container, class... Args>
auto insert(Container& c, Args&&... args) -> decltype (c.back()) {
c.emplace_back(std::forward<Args>(args)...);
return c.back();
}

Issue with variadic template template parameter pack

int B is a non-type template parameter. You have to declare Templates as template<class, auto, class> class if you want it to work with that particular class template, or redefine Obj to take a std::integral_constant to pass a value encapsulated in a type:

template<class A, class B, class C>
struct Obj {
A a_obj;
static constexpr int b_value = B::value;
C c_obj;
};

template<template<class...> class... Templates>
struct Foo;

template<>
struct Foo<Obj> {
Obj<void*, std::integral_constant<int, 5>, float> obj;
};

int main() {
Foo<Obj> foo;
}

Try it on godbolt.org.

Do Variadic Template Parameters Always Have to be Last?

Variardic arguments don't have to be last -- but it doesn't help you.

Your error is in the recursive call, when you try to set begin to be something different than 0. In that line, the compiler cannot figure out that your begin is supposed to be the std::size_t parameter, and bails out.

This compiles fine even in gcc 5.1:

template <class... Tp, std::size_t begin = 0U>
auto foo(std::tuple<Tp...>& t) -> std::enable_if_t<begin == sizeof...(Tp), void> {
std::cout << '\n';
}

template <class... Tp, std::size_t begin = 0U>
auto foo(std::tuple<Tp...>& t) -> std::enable_if_t<begin < sizeof...(Tp), void> {
std::cout << '\n';
}

(I rewrote it to figure out where it was going wrong, so it is slightly different in unimportant ways).

The important way it differs it the lack of recursive call.

As an aside, your printing code is a bit awkward. Consider using something like for_each_arg:

template<class F, class...Args>
void for_each_arg(F&& f, Args&&...args) {
using discard=int[];
(void)discard{((
f(std::forward<Args>(args))
),void(),0)...,0};
}

either mix the above with std::apply or write your own:

namespace details {
template<class F, class Tuple, std::size_t...Is>
decltype(auto) apply( std::index_sequence<Is...>, F&& f, Tuple&& args )
{
return std::forward<F>(f)( std::get<Is>(std::forward<Tuple>(args))... );
}
}
template<class F, class Tuple>
decltype(auto) apply(F&& f, Tuple&& tuple) {
using dTuple = std::decay_t<Tuple>;
return details::apply(
std::make_index_sequence<std::tuple_size<dTuple>::value>{},
std::forward<F>(f),
std::forward<Tuple>(tuple)
);
}

template<class F, class Tuple>
decltype(auto) for_each_tuple_element( F&& f, Tuple&& tuple ) {
return apply(
[&](auto&&...args){
for_each_arg( std::forward<F>(f), decltype(args)(args)... );
},
std::forward<Tuple>(tuple)
);
}

and now you don't have a recursion depth equal to the number of elements in your tuple.

template <class Tuple>
void foo(Tuple&& tuple) {
for_each_tuple_element(
[](auto&& arg){ std::cout << decltype(arg)(arg); },
std::forward<Tuple>(tuple)
);
std::cout << '\n';
}

live example.

template function parameter pack not at the end of the list

Variadic template arguments are greedy, so if you try and explicitly specify the template arguments for a template that has a variadic argument, once the explicit arguments start being allocated to the variadic pack, all remaining arguments are allocated to that pack. In this case A2 is not a type, so when the compiler tries to allocate it to the Ts... pack, it causes an error.

You could overload your template, allowing the enum to be specified as the first parameter:

template <A a,typename...Ts >
void foo(Ts...ps) { std::cout << sizeof...(ps); }
template <typename...Ts>
void foo(Ts...ps) { foo<A2>(ps...); }

foo<int,int>(1,2); // this compiles
foo<A2,int,int>(1,2); // this compiles

Variadic templates, parameter pack and its discussed ambiguity in a parameter list

In the class template, a prospective template argument list C<a,b,c,d,e,f> needs to match

template<typename T, typename... A, typename S>

in which ...A is just floating in a parameter list.

But in the specialization, what needs to be matched is not the list of template variabled but rather the pattern:

C<T(A...), S>

which is easy because the A... is delimited.

So in the template specialization, the list of parameters is just an inventory of symbols, some scalar and some parameter packs, which will appear in a pattern.

Multiple Variadic Parameter Pack for Template Class

In the discussion comments you expressed a willingness to consider some kind of indirection, or "a wrapper of some kind for the attribute list".

A lightweight std::tuple-based wrapper, together with specialization, might work here:

template <typename attribute_tuple, APITypes APIType,
typename policy_tuple> class IShader;

template <AttributeType... Attributes, APITypes APIType,
class... Policies>
class IShader<std::tuple<Attributes...>, APIType,
std::tuple<Policies...>> : public Policies... {

// ...

};

The goal here is to use a template instance along the lines of:

IShared<std::tuple<Attribute1, Attribute2>, APITypeFoo,
std::tuple<Policy1, Policy2>> ishared_instance;

And cross your fingers that this is going to match the specialized template declaration, at which point both parameter packs are available for the template specialization to use, individually.



Related Topics



Leave a reply



Submit