Andrew Sutton <andrew.n.sut...@gmail.com> writes:

| > | Previous discussion seemed to indicate that saving constraints with
| > | template decls was preferable.
| >
| > Yes; exactly.
| >
| > | The committed version in the branch carries those constraints in
| > | template_info for the temploid.
| >
| > Yes; I think that is good.
| 
| So we should associate constraints with both TEMPLATE_DECLs and 
TEMPLATE_INFOs?
| 
| Storing constraints with TEMPLATE_INFO could require more memory since
| each instantiation generates a new one. It doesn't look like that
| structure has any free trees anyways, so we'd have to add a field
| (which is what we've done now).

We don't want to unnecessarily duplicate the information...
The constraints have to go with TEMPLATE_DECLs directly or in the
TEMPLATE_INFO.  But, we don't want a "distributed fat."

| Concepts lite shouldn't need to track instantiated constraints,
| anyway. Constraints are either satisfied or they aren't.

Exactly right.  For partial ordering, we use the original form to decide
most specialized -- mirroring what we do for non-constrained templates.
And when we do that we use the constraints from the source-level decls.

| > | > I think we need to remember them for duplicate_decls or other routines
| > | > that either check or use ODR-derived properties.
| > |
| > | This doesn't seem to be a problem. IIRC, the instantiated declarations
| > | are differentiated by their template constraints, not their
| > | instantiated constraints.
| > |
| > | Differentiating by instantiated constraints may not be viable,
| > | anyways. Especially, if the instantiation folds them to only "true" or
| > | "false".
| >
| > Hmm, I am lost here.  Could you clarify with examples?
| 
| template<typename T>
| struct S {
|   void f() requires Input_iterator<T> { }
|   void f() requires Bidirectional_iterator<T> { }
| };
| 
| Probably an unlikely example, but anyways...

Ah, I think last time we discussed this, it was something like

   template<typename T>
    struct vector {
       void push_back(T&&) requires MovableOnly<T>;
       void push_back(const T&) requires CopyConstructible<T>;
    };

but they are already distinguished by the parameter lists so that is not
an issue.

The cases were you can't distinguish the instantiated decls based on their
types correspond to "perfect" (partial) specializations; we should use the
existing  model for those in this specific case.  That means going back
to the abstract form (the associated temploids) and 

| The class S<T> has two declarations of f, "S<T>::f() requires
| Input_iterator<T>" and "S<T>::f() requires Bidirectional_iterator<T>".
| When you instantiate S, say S<int*>, we instantiate the nested
| declarations. They are made distinct on the basis of the dependent
| constrains. If you try to distinguish them on the basis of their
| instantiated constraints you could have:
| 
| template<>
| struct S<int*> {
|   void f() requires true;
|   void f() requires true; // Oops. Not desirable.
| };

They look like partial specializations of member templates
(which they morally are); we should treat them as such.  You have
similar situation if you put them at non-class scope.

-- Gaby

Reply via email to