Open Eisenwave opened 4 months ago
I was expecting that we could say this is editorial, on the basis that the declaration in question would be both a class template and a variable template, so [temp.pre]/5 would apply. But it seems we have a larger issue here: we never actually define the term "class template" at all. The definition is not trivial:
// class template
template<typename> class X;
// not class template
template<typename> class X y;
But it seems we have a larger issue here: we never actually define the term "class template" at all.
Yeah, I also find the "editorial definition" for class templates to be in need of work. I'm not sure if fixing that would be too much for a core issue though; perhaps a paper is required, especially if the fix is meant to apply to all sorts of templates which may not be properly defined right now.
The definition is not trivial:
It may still be relatively easy if you leave all these cases to the logic which decides whether something is a class or variable. I.e. define a class template to be a template-declaration where the declaration declares a class.
If so, the following would be a variable template:
template <int>
struct S {} y;
I believe the rule of thumb should be
It's the same kind of declaration as if you removed the template-head.
I believe that the declaration would be simultaneously a class declaration and a variable declaration, which would make it ([temp.pre]p5 notwithstanding, and ignoring the fact that "class template" is not defined) simultaneously a class template and variable template. That way, [temp.pre]p5 is correct as written.
I can't find a definition for "class declaration" either, but a note in [class.name] does distinguish a class declaration and an elaborated type specifier, seemingly even if that elaborated type specifier also declares the class name. I believe that that note is incorrectly worded, as otherwise
template <typename>
class S;
would (presumably) not be a class template.
I believe the rule of thumb should be
It's the same kind of declaration as if you removed the template-head.
I don't think that's quite what we want. Specifically:
// This is a class declaration.
struct A *x;
// This is not a class template declaration.
template<typename T> struct A *y;
template<typename T> struct B;
// This is not a class declaration (it's not even valid).
struct B<int*>;
// This is a class template declaration.
template<typename U> struct B<U*>;
That is, for elaborated-type-specifiers we want both cases in [dcl.type.elab]/2 and not the case in [dcl.type.elab]/3, whereas "declares a class" is the first case in [dcl.type.elab]/2 plus the case in [dcl.type.elab]/3.
Perhaps we should say something like (in [temp.pre]/3):
A class template is a declaration introduced by a template declaration whose declaration is a simple-declaration that either contains a class-specifier in its decl-specifier-seq or comprises solely of an elaborated-type-specifier.
but maybe we should make sure all the different kinds of template are properly defined. Then perhaps [temp.pre]/5:
In a template-declaration, explicit specialization, or explicit instantiation the init-declarator-list in the declaration shall contain at most one declarator. When such a declaration is used to declare a class template, no declarator is permitted.
... can be replaced with "A template-declaration shall declare exactly one template." or similar :)
CWG2862
Hm. I think my suggestion doesn't quite work:
template<typename T> struct A {
struct B;
};
// This is not a class template, but I think the new definition says that it is.
template<typename T> struct A<T>::B {};
(The existing definition of "variable template" is wrong in the same way.)
Maybe something more like this:
If the declaration in a template-declaration introduces an entity with a dependent nested-name-specifier that is equivalent to the injected-class-name of a class template or class template partial specialization T, and the template-head of the template-declaration is equivalent to that of T, the template-declaration declares a member of T, and the nested-name-specifier is treated as a non-dependent reference to T for the purpose of further interpreting the declaration. Otherwise:
- A class template is introduced by a template-declaration whose declaration is a simple-declaration that either contains a class-specifier in its decl-specifier-seq or comprises solely of an elaborated-type-specifier.
- A function template is introduced by a template-declaration whose declaration declares a function.
- An alias template is introduced by a template-declaration whose declaration is an alias-declaration.
- A variable template is introduced by a template-declaration whose declaration declares a variable. [ Example:
template<typename T> struct A { template<typename U> struct B; template<typename U> struct B<U*> { template<typename V> void f(); }; }; template<typename T> // #1 template<typename U> // #2 template<typename V> // #3 void A<T>::B<U*>::f() {}
- The template-declaration #\1 declares a member of the class template
A
, becauseA<T>
is equivalent to the injected-class-name ofA
.- The template-declaration #\2 declares a member of the class template partial specialization
A<T>::B<U*>
, becauseA<T>::B<U*>
is equivalent to the injected-class-name of the partial specialization whenA<T>
is treated as a non-dependent reference to the primary templateA
.- The template-declaration #\3 declares a function template that is a member of the class template partial specialization. -- end example]
... though I'm not sure about the exact phrasing here, particularly the "treated as non-dependent" part. I think we need soemthing to say "you actually need to do name lookup inside this nested-name-specifier even though it's a template with dependent arguments".
Looks like an improvement; I've integrated your suggestion into the core issue, with two minor tweaks:
- It's not /any/ dependent nested-name-specifier that gets the special treatment (in particular, the return type doesn't get a special treatment for this paragraph), it's just the one in the declarator-id.
I think we need to cover more than that -- we should also apply this to the nested-name-specifier in an elaborated-type-specifier, class-head-name, or enum-head-name, as well as a nested-name-specifiers that is a prefix of another nested-name-specifier with this treatment.
Also:
A template-declaration shall declare exactly one template or member of a template.
Hm... The special nested-name-specifier rules you gave are exactly those of a "declarative nested-name-specifier" in [expr.prim.id.qual] p2 --- except for the elaborated-type-specifier case. And [dcl.type.elab] doesn't seem to mention a case where a qualified elaborated-type-specifier would be able to declare a template. (in particular, you can't declare members out-of-class unless that declaration is a definition, in which case we're in the class-head-name situation, not the elaborated-type-specifier case.)
CWG2862 is updated.
[dcl.type.elab] doesn't seem to mention a case where a qualified elaborated-type-specifier would be able to declare a template.
Interesting. We almost hit this situation for partial specializations of member templates:
template<typename T> struct A {
template<typename U> struct B;
};
// Error, can't declare a specialization of a member of an unspecialized template.
template<typename T> template<typename U>
struct A<T>::B<U*>;
... but not quite. We do hit this for template friends:
struct Z {
template<typename T> friend struct A<T>::B;
};
But [temp.friend]/5 has custom rules for that, so perhaps we don't need to address it here?
Yeah, the temp.friend rules seem to be very specific and add additional permission via "may".
I feel that there should be an example that illustrates the implication of the "treated as a non-dependent reference to C for the purpose of further interpreting the declaration" part of this wording (in particular, why the word "non-dependent" needs to be there). Otherwise I can't tell what it's supposed to be doing.
Full name of submitter (unless configured in github; will be published with the issue):
Reference (section label): [temp.pre] p5
Issue description
It is unclear whether the following construct is permitted (rejected by all compilers):
This could be
Suggestion resolution
If this construct is intended to be ill-formed, modify [temp.pre] p3 as follows:
If this construct is intended to be well-formed, modify [temp.pre] p5 as follows: