The constrains on template template-parameters seem to be ignored

I’m trying to add the support for constrained template template-parameters to Clang, but I’m not sure if my understanding is right.

Take the following snippet for example:

template<bool B> concept C = B;
template<template<bool B> requires C<B> typename U> struct X {
	template<bool B> static U<B> V;
};
template<bool B> requires C<B> struct Y {};
template<bool B> struct Z {};

The class X has a constrained template template-parameter, and the following specialization of the member V is undoubtedly correct:

template<> template<> Y<true> X<Y>::V<true>;

However, the following two are also correct in the views of GCC and MSVC, which looks to me that the constrain on the template template-parameter is ignored:

template<> template<> Z<true> X<Z>::V<true>;
template<> template<> Z<false> X<Z>::V<false>;

Are the above two allowed in the proposal?

@erichkeane had some questions about contrained template template parameters a while back and I suspect he’ll have more insight here. He is out of the office for a few days though, so might not respond quickly.

[temp.expl.spec]p11 seems relevant though the associated constraints, according to [temp.constr.decl]p3, don’t appear to include constraints on template template parameters.

[Note 2: An explicit specialization of a constrained template is required to satisfy that template’s associated constraints ([temp.constr.decl]). The satisfaction of constraints is determined when forming the template name of an explicit specialization in which all template arguments are specified ([temp.names]), or, for explicit specializations of function templates, during template argument deduction ([temp.deduct.decl]) when one or more trailing template arguments are left unspecified.end note]

Both gcc and MSVC accept the following example in which the implicit instantiations of X and X::V violate the template template parameter constraints.

template<bool B> concept C = B;
template<template<bool B> requires C<B> typename U> struct X {
	template<bool B> static U<B> V;
};
template<bool B> requires C<B> struct Y {};
template<bool B> struct Z {};
void f() {
  X<Y>::V<true>;
  X<Z>::V<true>;
  X<Z>::V<false>;
}

MSVC does reject the above example if the following cases are appended to the body of f(). That gcc doesn’t reject these cases seems likely to me to be a bug.

X<Y>::V<false>;
Y<false> yf;

I suspect there are multiple core issues worth filing in this area. I did a quick search, but the only ones I found are from the pre-C++11 concepts work (CWG 848 and CWG 849) and they were closed without resolution.

Perhaps, this could be classified as undefined or unspecified behavior, while cases like X<Y>::V<false> should be ill-formed.

I don’t think undefined or unspecified behavior is appropriate in this case since all of the information needed to behave in a well-specified way is available. What is needed is a better understanding of the intent in the standard (and the standard could use some updates to clarify the intended behavior).

I noticed that Clang accepts the following snippet:

template<typename T> concept C = true;
template<template<C T> typename U> struct X {};
template<typename T> struct Y {};

X<Y> xy;

A tailing form like template<template<typename T> requires C<T> typename U> struct X {}; should behave the same.

1 Like

I agree as do other implementations: Compiler Explorer

@erichkeane, known issue perhaps?

So I did a bit of work on implementing the remainder of that here: ⚙ D110641 Implement P0857R0 -Part B: requires clause for template-template params

I found that at the time I couldn’t come up with a reproducer that didn’t work there, but I didn’t try very hard. I asked on the WG21 reflector, and added the feedback to that as well.

Oh, this patch should be enough.

I don’t know if it that patch is ‘done’, or even ‘sufficient’, I ended up switching to working on the deferred concepts implementation (which has been an adventure since…) and got distracted, but that patch needs tests written still to confirm it DOES work (if in fact, it does).