This patch attempts to address issues related to member template
specializations.
As a quick review (incorporating direction from Issaquah):
- Full specializations of function, class or variable member templates can be declared at class or namespace scope.
- Partial specializations of a class or variable member template can be declared similarly.
- A full or partial specialization, or a primary member template can be explictly declared for an implicitly instantiated class specialization.
- if the primary template is so explicitly declared it does not inherit partial specializations from the unspecialized prototype template.
- if a full or partial specialization is so explicitly declared it does but wins out over other specializations that contains fewer empty template parameter lists.
- Explicitly declared specializations (full or partial) of member templates are only substituted into:
- a class specialization needs to have its type completed
- a variable specialization is referenced (to determine type), and then when its value (instantiate initializer) is needed.
- once a function template is selected, and its declaration is being instantiated.
- During substitution of any of the explicitly declared partial or full specializations if an error occurs, that declaration is skipped and is not considered either in the partial ordering, or explicit specialization search.
Following are some examples:
template<class T, class> struct A { template<class U> static A<U, T> var; template<class U> static U var<U*>; template<> static char var<char>; template<class U> struct B { template<class V1, class V2> V1* foo(V2); template<> typename T::type* foo<typename T::type>(U) { return 0; } }; template<class U> struct B<U*>; }; template<class T, class T2> template<> struct A<T, T2>::B<typename T::type> { }; template<class T, class T2> template<> struct A<T, T2>::B<float> { }; template<class T, class T2> template<class U> template<> char* A<T, T2>::B<U>::foo(T2) { return 0; } template<> template<class U> auto A<int>::var<U*> = [](U*) { }; template<> template<class U> auto A<char>::var = [](U*, char) { };
Additionally, explicitly declared specializations (partial or full) of constexpr
member templates always require an initializer when in-class:
- if they are then declared out of class, it is treated as a definition and can not have an initializer
- For variable templates (and their partial or full specializations) first declared out of line as constexpr they must have an initializer - and subsequently represent a definition.
For class templates, if a partial or full specialization is declared
after a potential use, it is an error.
For variables templates:
- if a partial or full specialization is declared after the 'type' of a variable specialization (that would have used the expicitly declared specialization) has been used, it is only valid if we can prove that the type will be the same.
- When the variable specialization's initializer is needed (that is its value is required), we once again search for the best specialization declaration, and if all is consistent (type matches, value has not been used etc.), we use its value.
Patch Implementation Notes:
I tried to comment the patch with code examples - but the comments do need
improvement. The patch is also short on good diagnostics.
I also introduced checking of class scope function specializations within
a dependent context by generating/inventing template arguments for each
enclosing template parameter list - so that we can compare function
templates, order them (requires their template depth to be zero)
and then link the full-specialization to its appropriate primary
template.
The alternatives were:
- to postpone all analysis in the dependent context (status quo) and only when the outer template arguments have been substituted, do the analysis of the full specializations (the problem with this is you can write some real non-sense in a dependent context that could never specialize any of your member function templates) and they would always potentially just get SFINAE'd out.
- consider teaching TemplateDeclInstantiator & TemplateInstantiator to do substitution of template parameters at specific template depths or focus only on certain depths when doing deduction (currently when we do partial ordering, the template delcaration's template parameters must have a depth of 0).
Not sure if I made the right choice, but it seems to work reasonably
well.
I also merged some of the logic for handling partial specializations
of class and variable templates.
Looking forward to some feedback.
Thanks!
Maybe isCXXClassMember()?
FIXME: How do we get here for something that's not instantiated from a member?