This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 116a. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2025-01-03


2980. Constraints on template template parameters

Section: 13.3  [temp.names]     Status: open     Submitter: Hubert Tong     Date: 2024-12-23

(From submission #658.)

Subclause 13.3 [temp.names] paragraph 8 specifies:

When the template-name of a simple-template-id names a constrained non-function template or a constrained template template-parameter , and all template-arguments in the simple-template-id are non-dependent (13.8.3.5 [temp.dep.temp]), the associated constraints (13.5.3 [temp.constr.decl]) of the constrained template shall be satisfied (13.5.2 [temp.constr.constr]).

For template template parameters, it is unclear how this provision interacts with instantiation or substitution. In particular, 13.4.4 [temp.arg.template] paragraph 3 allows for the template template parameter to be more constrained than the argument provided; however, once the parameter is replaced by the corresponding argument, the constraints of the parameter seemingly disappear. Consider:

  template <typename T>
  concept C = false;

  template <typename> struct Q;

  template <template <C> class TT>
  struct A {
    template <typename T> void f(TT<T> *) {}
  };

  template void A<Q>::f(Q<int> *);

Implementations accept this example. For the reverse situation, where 13.4.4 [temp.arg.template] paragraph 3 allows P to be unconstrained, also consider:

  template <typename T>
  concept C = false;

  template <typename> struct Q;

  template <template <typename> class TT, typename T>
  using Alias = TT<T>;

  template <template <C> class TT>
  struct A {
    Alias<TT, int> *p;
  };

  A<Q> a;

Implementations (other than gcc and clang, which seem not to implement the basic feature at all) appear to use the constraints of TT from A despite the simple-template-id emanating from Alias.

Maybe the constraints from template template parameters should be checked where they appear (including within an alias template) --- or are substituted into an alias template --- to form a simple-template-id once the arguments are known. That is, the associated constraints of both

are checked for simple-template-ids appearing in an alias template.

Also consider:

  template <typename T>
  concept C = true;

  template <typename T>
  concept CandMore = C<T> && true;

  template <typename> struct Q;

  template <template <C> class P, typename T>
  using Alias = P<T>;

  template <template <CandMore> class TT>
  struct A {
    Alias<TT, int> *p;  // #1
  };

  A<Q> a;

The P for Alias is not at least as specialized as the argument TT given at #1, if we take the TT constraints as written. However, the actual argument for TT may be such that P is at least as specialized as the argument TT. There is implementation divergence.