Member template definition not instantiated despite need for complete type

58 views
Skip to first unread message

razvyb...@gmail.com

unread,
Apr 15, 2018, 12:48:05 PM4/15/18
to ISO C++ Standard - Discussion
I tried to specialize a function member of a template class which is itself member of another template class.
I used the following syntax:

template<class T1>
struct A{
    template<class T2>
    struct B{
        void f() {}
    };
};

template<>
template <class T2>
void A<int>::B<T2>::f(){}

With the most recent versions of both gcc and clang I get the following error:

error: invalid use of incomplete type 'struct A<int>::B<T2>'
 void A<int>::B<T2>::f(){}

It is as if the definition of the class template A<int>::B is not instantiated, only the declaration. I know that normally, if the definition is "not needed" it will not be instantiated ([temp.inst] p.2), but in this case it is needed.

I thought this would be correct due to the following paragraphs from the standard:
  •  [temp.expl.spec] p.17: In an explicit specialization, it allows for some class templates to remain unspecialized (under some conditions)
  •  [temp.inst] p.3: It says the following: "[...] the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist [...]". I believe when I try to define a member of a template class, the definition of the member class needs to exist, therefore this definition should be instantiated.

I see that in the example present in [temp.expl.spec] p.17 they also explicitly specialize the class template A<int>::B, so maybe there is something in the standard that states that the definition of a member template is not instantiated for an implicit specialization of the enclosing class template.

However, I do not see any difference between this and the following example, which compiles fine:

template<class T>
struct U
{
    struct I
    {
        void f();
    };
};

template<>
int U<int>::I::f() {}

It seems that in this example, the definition of U<int>::I is instantiated, therefore I can define U<int>::I::f.

Could you please tell me what in the standard makes the first example invalid and the second example valid?

Thank you.

Richard Smith

unread,
Apr 15, 2018, 5:20:39 PM4/15/18
to std-dis...@isocpp.org
On 15 April 2018 at 09:48, <razvyb...@gmail.com> wrote:
I tried to specialize a function member of a template class which is itself member of another template class.
I used the following syntax:

template<class T1>
struct A{
    template<class T2>
    struct B{
        void f() {}
    };
};

template<>
template <class T2>
void A<int>::B<T2>::f(){}

With the most recent versions of both gcc and clang I get the following error:

error: invalid use of incomplete type 'struct A<int>::B<T2>'
 void A<int>::B<T2>::f(){}

It is as if the definition of the class template A<int>::B is not instantiated, only the declaration. I know that normally, if the definition is "not needed" it will not be instantiated ([temp.inst] p.2), but in this case it is needed.

I thought this would be correct due to the following paragraphs from the standard:
  •  [temp.expl.spec] p.17: In an explicit specialization, it allows for some class templates to remain unspecialized (under some conditions)
Here's the relevant wording in full:

"In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well".

Your example is ill-formed according to this rule: you can't explicitly specialize the class member template A<int>::B<T2>::f because the enclosing class template A<int>::B<T2> is not explicitly specialized.

But we might ask what circumstances this wording *does* allow. I think the answer is, essentially, none. The example makes clear that A<Y>::B<double>::mf2() is not a valid target for such an explicit specialization (where A<Y> is the primary template) because it "specializes" the B<double> member template specialization of the unspecialized template A<Y>. Given that understanding of the wording, we can conclude that if any component of the nested-name-specifier is "specialized", then all the components to the left must be. So there are really only two things that are valid here:

1) Specializing a template for a specific set of template arguments, where all enclosing class template specializations are non-dependent.
2) Specializing a member of a non-dependent class template specialization.

For what it's worth, [temp.expl.spec]p17's wording seems like an extremely strange and roundabout way to specify this, if indeed that's what it means.
  •  [temp.inst] p.3: It says the following: "[...] the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist [...]". I believe when I try to define a member of a template class, the definition of the member class needs to exist, therefore this definition should be instantiated.

I see that in the example present in [temp.expl.spec] p.17 they also explicitly specialize the class template A<int>::B, so maybe there is something in the standard that states that the definition of a member template is not instantiated for an implicit specialization of the enclosing class template.

However, I do not see any difference between this and the following example, which compiles fine:

template<class T>
struct U
{
    struct I
    {
        void f();
    };
};

template<>
int U<int>::I::f() {}

It seems that in this example, the definition of U<int>::I is instantiated, therefore I can define U<int>::I::f.

Could you please tell me what in the standard makes the first example invalid and the second example valid?

Thank you.

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+unsubscribe@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.

razvyb...@gmail.com

unread,
Apr 15, 2018, 5:48:55 PM4/15/18
to ISO C++ Standard - Discussion
Thank you for your answer.
However, I have a follow-up question.

Your example is ill-formed according to this rule: you can't explicitly specialize the class member template A<int>::B<T2>::f because the enclosing class template A<int>::B<T2> is not explicitly specialized.

I believe A<int>::B<T2>::f is not a class member template, it is just a member (function) of a class template (which is member of another class template), no? If so, this exception should not apply to this case.

I agree about the wording of [temp.expl.spec] p. 17. I think it is going to be re-written: http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#529.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.

razvyb...@gmail.com

unread,
Apr 19, 2018, 1:28:34 PM4/19/18
to ISO C++ Standard - Discussion, razvyb...@gmail.com
I read that paragraph ([temp.expl.spec] p.17) multiple times, but what I manage to understand from it is that explicitly specializing a class template without specializing its enclosing class templates is prohibited only in the case of explicitly specializing class member templates.

However, in the proposed resolution of the issue that I linked to in my previous comment (http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#529), the wording is changed as to indicate that it is never allowed to have an unspecialized class template followed by a specialized one. I believe I am misunderstanding the exception case from paragraph 17. I would appreciate if you could point my mistake. Thank you.
Reply all
Reply to author
Forward
0 new messages