问题
The following source code is brought from: Understanding partial specialization of inherited nested class templates
#include <type_traits>
struct Base
{
template<class U, class _ = void> struct Inner: std::true_type {};
template<class _> struct Inner<char, _>: std::false_type {};
};
struct Derived : Base
{
};
template<class _> struct Derived::Inner<int, _>: std::false_type {};
I had an issue about specializing inherited class, so I googled, and find out the question above. The source code in the question above compiled w/o any problem in gcc/clang, but msvc refuses to compile it, issuing C2427 (see https://msdn.microsoft.com/en-us/library/10het5hx.aspx).
Situation of the above (specialize the nested template class of a non-template class) is quite different from the situation described in https://msdn.microsoft.com/en-us/library/10het5hx.aspx (define the nested non-template class of a template class), I think.
Which one of msvc vs. gcc/clang is wrong? Or just the standard is so unclear to specify this behavior?
I hope msvc is wrong...
回答1:
Clang and GCC are wrong, and MSVC and EDG are right to reject that partial specialization definition.
A partial specialization is itself a template, and a class template definition is syntactically constructed in terms of a class definition (in grammar terms, a class-specifier). Within such a definition, Derived::Inner<int, _>
is a class-head-name, with Derived::
being a nested-name-specifier.
[9p11] in the Standard says:
If a class-head-name contains a nested-name-specifier, the class-specifier shall refer to a class that was previously declared directly in the class or namespace to which the nested-name-specifier refers, or in an element of the inline namespace set (7.3.1) of that namespace (i.e., not merely inherited or introduced by a using-declaration), and the class-specifier shall appear in a namespace enclosing the previous declaration. [...]
So, you have to use Base::Inner<int, _>
.
As noted in the comments, the quote above applies to class template explicit specialization definitions as well (their grammar production also ends up using class-head-name).
The following doesn't apply directly to your example, but I found it worth mentioning.
Note that the quote above refers to class template (or explicit specialization) definitions, not declarations such as
template<class _> struct Derived::Inner<int, _>;
Syntactically, struct Derived::Inner<int, _>
in there is an elaborated-type-specifier, to which the paragraph above doesn't apply. So, the Standard wording technically allows such declarations.
This doesn't seem to be an oversight: the wording above was introduced by the resolution of DR284, which includes the comment:
The sentiment was that this should be required on class definitions, but not on elaborated type specifiers in general (which are references, not declarations). [...]
The proposed resolution included elaborated-type-specifiers, but those were removed from the final wording.
However, neither MSVC nor EDG accept such declarations (and frankly I'd find it confusing if they did). The comment in the DR seems to indicate that the intent was to allow only elaborated-type-specifiers that are not also declarations, but it looks like this wasn't reflected in the wording (a Standard bug, I think).
来源:https://stackoverflow.com/questions/38215737/specialization-of-inherited-nested-template-class