Search code examples
c++templateslanguage-lawyervariadic-templatesparameter-pack

Variadic template parameter with variadic constraint


I have a template-head of the form template<C<T>... U>, where C is a concept and T is a pack of types. U will have to be its own pack, but how exactly is this parsed?

For example https://godbolt.org/z/MhG9YqsdK:

#include <concepts>

template<typename... T>
struct F {
    template<std::same_as<T>... U>
    static void f(U... args, void*);
};

int main() {
    F<int, char>::f(1, 'x', nullptr);
}

I would think that std::same_as<T> is a pattern for an expansion, so this expands to two template arguments, like:

template<>
struct F<int, char> {
    template<std::same_as<int> U0, std::same_as<char> U1>
    static void f(U0 arg0, U1 arg1, void*);
};

Which is what clang seems to do. What gcc seems to do is transform it like:

template<typename... T>
struct F {
    template<typename... U> requires (std::same_as<U, T> && ...)
    static void f(U... args, void*);
};

which makes U a new pack in a non-deduced context (And a SFINAE error when U can't be deduced and is an empty pack and T isn't).

Which compiler is correct? I would assume [temp.variadic]p5 is what I am looking for if this was actually a pack expansion, but I'm not sure if it applies. I also can't find where in the standard constrained variadic parameter packs are defined to see if gcc's behaviour is correct.


Solution

  • This looks like a GCC bug to me, it should be a pack expansion:

    [temp.param]/17

    ... A type parameter pack with a type-constraint that contains an unexpanded parameter pack is a pack expansion. ...