8

With concepts, C++20 provides nice syntax like

template<typename T>
concept SomeConcept = true; // stuff here


template<typename T>
requires SomeConcept<T>
class Foo;

template<SomeConcept T>
class Foo;

where the two ways of concept restricting the class are equivalent, but the latter is just more concise.

If i now have some template template concept like

template<template<typename> typename T>
concept SomeOtherConcept = true; // stuff here

template<template<typename> typename T>
requires SomeOtherConcept<T>
class Foo;

i do not know the non-verbose (concise / short) syntax for this without an requirement clause, as things like

template<template<typename> SomeotherConcept T>
class Foo;

template<template<SomeOtherConcept> typename T>
class Foo;

did not work, so

What is the correct syntax for declaring such a template template class with a concept restriction to the template template parameter?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982

2 Answers2

7

What is the correct syntax for declaring such a template template class with a concept restriction to the template template parameter?

The only way to write a constraint that depends on a template template parameter or a non-type template parameter is with a requires-clause. The shorter type-constraint syntax is only available for concepts that constrain types (hence the name type-constraint):

template <typename T> concept Type = true;
template <template <typename...> class Z> concept Template = true;
template <auto V> concept Value = true;

// requires-clause always works
template <typename T> requires Type<T> struct A { };
template <template <typename...> class Z> requires Template<Z> struct B { };
template <auto V> requires Value<V> struct C { };

// type-constraint only for type concepts
template <Type T> struct D { };

// abbreviated function template definitely only for type concepts
void e(Type auto x);
Barry
  • 286,269
  • 29
  • 621
  • 977
  • So, if I want to use two type-constraints at once, I either need to use a requires-clause instead, or write my own concept which boils down to: `myconcept = concept1 and concept2`? – 303 Jun 12 '22 at 11:44
  • @303 That's correct. You could also mix-and-match, like `template requires Concept2` for instance. – Barry Jun 12 '22 at 21:01
2

This is a trick that I have used before.

Define a lambda in the primary expression using a noop-like function as shown:

void noop(auto) {}

//...

template<typename T>
concept SomeConcept = true;

/*
template <template<typename>SomeConcept T>
struct Example {};
*/ //does not work

template <template<typename>typename T>
  requires requires() {
    {
      noop(
        []<typename TArg> requires SomeConcept<typename T<TArg>> (){}
      )
    };
  }
struct Example {};
  • 1
    How is that massive `requires requires ...` better than `requires SomeConcept`? – Caleth Jul 14 '21 at 16:33
  • @Caleth `templatetypename` is not convertible to `typename`. So its better then what you suggested because what your suggested does not even compile. –  Jul 14 '21 at 16:53
  • Sorry, I meant `requires SomeOtherConcept`, which is what was being asked about – Caleth Jul 14 '21 at 16:56
  • @Caleth `requires SomeOtherConcept` would be the same as writing `template ` which is **not** what is being asked about. You could specify that the "return" of `templatetypename T` has a static member that adheres to the concept `SomeOtherConcept` to solve this issue. (the static member hack being required because `templatetemplatetypename` is invalid) –  Jul 14 '21 at 17:05
  • @Vye, not if `T` is `template – Object object Jul 14 '21 at 18:20
  • Did this compile in a previous compiler version? It fails on GCC (12.2.1 20230201) with "parse error in template argument list"..."template argument 1 is invalid" on the lambda line. – xaxazak Apr 11 '23 at 00:05