Search code examples
c++c++20variadic-templates

Parameter pack cannot be expanded


template<integral auto n1, decltype(n1) n2, class callable_t, class... args_t> requires (n1 <= n2)
constexpr void const_for(callable_t&& callable, args_t&&... args)
{
    callable<n1>(forward<args_t>(args)...);

    if constexpr (n1 < n2) { callable<n1 + 1>(forward<args_t>(args)...); }
}

I can't understand what is the problem here. But in visual studio 2022, it gives the following errors (line 4 and 6):

Error C2760 syntax error: '...' was unexpected here; expected ')'

Error C2760 syntax error: ')' was unexpected here; expected ';'

Error C3878 syntax error: unexpected token ')' following 'expression_statement'

etc.


Solution

  • Lemme reduce to just one argument to make this clearer:

    callable<n1>(arg);
    

    What this means, given an object (i.e., not a function template, not a variable template, but an actual object) is:

    (callable < n1) > (arg)
    

    That is, the < is parsed as the less-than operator, not as an introducer for template arguments. This is one of the issues with using <>s for both contexts (and why the D language, for instance, uses ! - which is one of those syntactic choices that seems incredibly jarring when you first see it but is definitely a good idea).

    In some contexts, you can use the template keyword to disambiguate that no, you do actually mean to introduce template arguments. You'd see something like:

    using A = typename B::template C<D>;
    

    But here, that's not an option - because, again, callable is an object, and objects can't take template arguments.

    The solutions here are:

    • either explicitly invoke the call operator, which would be callable.operator()<n1>(arg)
    • pass the template parameter as an argument, that is: callable(integral_constant<decltype(n1), n1>(), arg)

    The latter has a lot going for it and is a pretty nice pattern, so I would recommend doing that. It becomes nicer with an alias:

    template <auto V>
    using constant = std::integral_constant<decltype(V), V>;
    

    Because then callable(constant<n1>(), arg).