Can a class template explicit specialization also declare something else?
Asked Answered
Y

2

11

It would be nice if this code were invalid. But it's conceptually sound, and GCC accepts it although Comeau doesn't:

template< typename > struct t;

template<> struct t< int > {} r; // Bad declarator! Don't pee on the carpet!

(Edit: the above compiles but r seems no to be declared into any scope, so it is essentially ignored.)

Explicit specializations populate a kind of nether region between templates and classes. The type declared by an explicit specialization is complete once it is defined. From the compiler's standpoint, it is not a template. If it were a parameterized template, declaring an object would be impossible. Consider §14/3:

In a template-declaration, explicit specialization, or explicit instantiation the init-declarator-list in the dec- laration shall contain at most one declarator. When such a declaration is used to declare a class template, no declarator is permitted.

What does "is used to declare a class template" mean? Clearly a primary template declares a class template. And a partial specialization does too, according to §14.5.5/1 (FDIS numbers):

A template declaration in which the class template name is a simple-template-id is a partial specialization of the class template named in the simple-template-id.

When it comes to explicit specializations, though, the Standard speaks in terms of a declaration preceded by the token sequence template<>. It looks like a template and it names a template-name, but it doesn't seem to declare a template.

The really bizarre thing is that §14/3 restricts the number of declarators to "at most one." A function template declaration, explicit specialization or instantiation must have exactly one declarator. Any declaration involving a class template must have exactly zero… except explicit specialization, which seems to fall through the cracks. Faithfully, GCC refuses to allow

template<> struct t< int > {} r, s; // Offer valid one per specialization.

I tend to agree with GCC's interpretation, nonsense as it may be. Unfortunately, it may be inhibiting its ability to detect missing semicolons. Please, let the number of allowed declarators be exactly zero!

Ypsilanti answered 10/6, 2011 at 9:9 Comment(2)
Consider template<typename T> template<typename U> struct A { struct B { }; }; template<> template<typename T> struct A<int>::A { };. Here, an explicit specialization happens for a member template of the instantiation of A<int>, but without specializing the member template itself (there is not a real term for this kind of explicit specializations. At one point, the spec uses the terms "specialized as a template" once when it refers to such cases: "However, template<> is used in defining a member of an explicitly specialized member class template that is specialized as a class template.").Yawl
Out of curiosity, I tried compiling it with clang++. It prints an error "extraneous 'template<>' in declaration of variable 'r'". Obviously, when tempalte<> is ommited, it gets an error "template specialization requires 'template<>'".Vitalis
Y
1

Explicit specialization and explicit instantiation do not declare a template. They declare a template-id which refers to a specialization, which is a class.

However, this doesn't validate my example. The problem is that everything declared following template or template<> is part of the explicit instantiation or specialization, respectively. Only certain types of entities may be specialized or instantiated, and previously-undeclared names aren't one of them.

Consider these examples making gratuitous, but legal use of elaborated-type-specifiers (§7.1.5.3):

template< typename T > struct s;
template< typename T > s< int > *f() {}

template<> struct u *f< char >(); // struct u is declared
u *p = 0; // see, we can use its name now.
template<> struct s< int > *f< int >(); // s<int> declared but not specialized
template struct s< int > *f< long >(); // s<int> declared but not instantiated

As far as I can tell, the Standard is fuzzy about specifying which declared name is the one specialized. The language does weakly imply that each such declaration applies to only one template: §14.7.2/2

If the explicit instantiation is for a class, a function or a member template specialization…

and §14.7.3/2

An explicit specialization shall be declared in the namespace of which the template is a member…

The only way to resolve this is to ignore the type declaration if the declarator also specifies a legal instantiation/specialization.

Getting to the point, the examples in the question specify illegal specializations in the declarator and then expect the compiler to backtrack and specialize the type instead. Given the explicit lists of what specializations and declarations are allowed to do in §14.7.2/1 and §14.7.3/1, it seems more reasonable to complain about template<> struct t< int > {} r; that r is not a function template, member function template, static data member of a class template, etc.

Ypsilanti answered 20/6, 2011 at 18:30 Comment(0)
D
13

Several points: first, explicit specializations are not in a nether region between templates and classes; an explicit specialization is a class, period. The only relation is has with templates (except for the funny name) is that it will be used instead of a template instantiation if the template is to be instantiated on the specialization type.

Secondly, if there is a problem with the paragraph in §14/3 that you cite, it is that it includes explicit instantiation; an explicit instantiation is a class definition, and if

struct S {} s, *p;

is legal,

template<> struct T<int> {} s, *p;

should be too. (I would argue against allowing either, but that train has already left the station, and since C allows the first, we're stuck with it.)

Otherwise, the statement in §14/3 is a bit irrelevant. A function template must have exactly one declarator, and a class template exactly zero; there's no need to try to englobe them both in some "at most one" gobbledygook. (If I were designing the language from scratch, I'd not allow any declarator in a declaration which defined a class or enum type. But again, it's too late for that.)

And I agree that it's a bother:

template<> struct T<int> {};    //  Requires a ';'
template<> void f<int>() {}     //  ';' forbidden

(At least C++11 will allow a semicolon after the function definition.)

Damage answered 10/6, 2011 at 9:43 Comment(8)
Apologies for the moderately hyperbolic prose. This answer jibes well with my understanding and opinions (notwithstanding the desire to fix the GCC bug in a particular way), but a reference would be nice. After all, Comeau does reject my first example.Ypsilanti
@Ypsilanti No apologies needed. I can understand your point of view. With regards to my understanding: I have always understood that an instantiation of a class template (regardless of whether is is explicit or not) is a class, and an instantiation of a function template is a function. But now that I'm looking for it in the standard, I can find no statement to this effect; perhaps the very first sentence: "A template defines a family of classes or functions" (so each instance is a member of the family, i.e. either a class or a function)?Damage
I think that semantically, it's specified that an explicit instantiation yields a class or function as needed (an explicit instantiation can even yield a template again. example: template<typename T> struct A { template<typename B> void f() { } }; template struct A<int>;, instantiates the member template A<T>::f to yield another member template A<int>::f). The FDIS says at 14.7p4: "A specialization is a class, function, or class member that is either instantiated or explicitly specialized (14.7.3).".Yawl
But I cannot find either where it says that an explicit instantiation is a declaration of the entity that it creates. That is, I can't find where it says that the above template struct A<int>; is a class declaration or class definition. I don't even know whether it should say such. My standardese experience isn't enough to have a definite answer.Yawl
@James: By the way, you confounded explicit instantiation with explicit specialization in the second paragraph. Explicit instantiation likewise declares a single specialization of an existing template, so it may be in the same class as explicit specialization in terms of allowing declarators.Ypsilanti
I've set a 150-point bounty on this question. I don't think this is a good answer because it focuses on what the specialization is, which is irrelevant. Every specialization of a class/function template is a class/function. "When such a declaration is used to declare a class template" seems to me to be syntactic requirement intended to mean "if the declaration contains an elaborated-type-specifier where the principal identifier is a template-name." I need help decoding the intent and meaning of this requirement, not an opinion or a wishlist (I have those already :v) ).Ypsilanti
@Ypsilanti If it says one thing, why do you want it to say another. As you say, every specialization of a class or function template is a class or a function, respectively. An explicit specialization is a specialization. It is not a declaration used to declare a class template; it is a declaration which declares or defines a class or a function. Except where there are special rules for explicit specialization (e.g. at most one declarator), it follows the rules of any other declaration or definition of a class or function.Damage
Because the Standard contains many rules that apply only to explicit specializations and/or instantiations, they do not follow the rules of any other declaration or definition. See my answer. For what it's worth, much of my confusion resulted from the notion of a declaration that does not introduce any new name. I'm still not sure the standard is unambiguous in specifying what identifier is explicitly specialized/instantiated or that the examples in my answer work as stated.Ypsilanti
Y
1

Explicit specialization and explicit instantiation do not declare a template. They declare a template-id which refers to a specialization, which is a class.

However, this doesn't validate my example. The problem is that everything declared following template or template<> is part of the explicit instantiation or specialization, respectively. Only certain types of entities may be specialized or instantiated, and previously-undeclared names aren't one of them.

Consider these examples making gratuitous, but legal use of elaborated-type-specifiers (§7.1.5.3):

template< typename T > struct s;
template< typename T > s< int > *f() {}

template<> struct u *f< char >(); // struct u is declared
u *p = 0; // see, we can use its name now.
template<> struct s< int > *f< int >(); // s<int> declared but not specialized
template struct s< int > *f< long >(); // s<int> declared but not instantiated

As far as I can tell, the Standard is fuzzy about specifying which declared name is the one specialized. The language does weakly imply that each such declaration applies to only one template: §14.7.2/2

If the explicit instantiation is for a class, a function or a member template specialization…

and §14.7.3/2

An explicit specialization shall be declared in the namespace of which the template is a member…

The only way to resolve this is to ignore the type declaration if the declarator also specifies a legal instantiation/specialization.

Getting to the point, the examples in the question specify illegal specializations in the declarator and then expect the compiler to backtrack and specialize the type instead. Given the explicit lists of what specializations and declarations are allowed to do in §14.7.2/1 and §14.7.3/1, it seems more reasonable to complain about template<> struct t< int > {} r; that r is not a function template, member function template, static data member of a class template, etc.

Ypsilanti answered 20/6, 2011 at 18:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.