c++language-lawyerc++20c++-conceptsparameter-pack

No concept subsumption with template parameter pack?


In C++20, concept subsumption refers to a concept being a superset of another concept. For example, in the following example, Fooable subsumes BetterFooable:

template <typename T>
concept Fooable = requires(T& t) {
    t.foo();
};

template <typename T>
concept BetterFooable = Fooable<T> && requires(T& t, int x) {
    t.foo(x);
};

The compiler is able to see that BetterFooable is defined to be Fooable and something else, and therefore, whatever type satisfies BetterFooable must also necessarily satisfy Fooable.

This allows us to partially specialise template classes:

template <Fooable T1>
struct MyStruct1 {};
template <BetterFooable T1>
struct MyStruct1<T1> {};

However, a specialisation of a class having a template parameter pack seems to be acceptable in neither GCC nor Clang, but acceptable in MSVC (godbolt link):

template <Fooable... Ts>
struct MyStruct2 {};

template <BetterFooable... Ts>
struct MyStruct2<Ts...> {};

The error produced by GCC is below, and the error produced by Clang is very similar:

<source>:15:8: error: partial specialization 'struct MyStruct2<Ts ...>' does not specialize any template arguments and is not more constrained than the primary template; to define the primary template, remove the template argument list
   15 | struct MyStruct2<Ts...> {};
      |        ^~~~~~~~~~~~~~~~
<source>:12:8: note: primary template here
   12 | struct MyStruct2 {};
      |        ^~~~~~~~~

Are GCC and Clang correct to reject this code, and are there any alternative ways to express concept subsumption in template parameter packs?


Solution

  • Are GCC and Clang correct to reject this code

    Currently, yes.

    and are there any alternative ways to express concept subsumption in template parameter packs?

    Currently, no.

    However, P2963 was accepted for C++26, whose motivation was exactly the example here. It's already implemented in Clang 19 at least.