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...
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).
Related
All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.
Consider the following class template A and class B which private defines a nested class C and enum class E:
template<typename T, typename U>
class A {};
class B {
class C {};
enum class E {};
};
According to [temp.explicit]/12:
The usual access checking rules do not apply to names used to specify explicit instantiations. [...]
we may refer e.g. to private types such as B::C and B::E in the template argument list when specifying explicit instantiations:
// OK: explicit instantiation definition.
template class A<B::C, B::E>;
I'm trying to find the similar segment in the standard that specifies whether the same waiving of access right restrictions when specifying the template argument list for (partial and particularly explicit/full) specializations.
// Partial specialization.
template<typename U>
class A<B::C, U> {};
// Explicit(/full) specialization.
template<>
class A<B::C, B::E> {};
The partial specialization fails to compile in Clang
error: 'C' is a private member of 'B'
whereas GCC accepts it. Possibly this is due to different instantiation rules between GCC and Clang. Both GCC and Clang accepts the explicit(/full) specialization.
I'm suspecting the explicit(/full) specialization is well-formed whereas the partial specialization is ill-formed (implicitly due to default rules). However, for the former, I have not been able to find a statement similar to that of [temp.explicit]/12 in [temp.expl.spec].
Question
Where, in the standard, is it specified (implicitly or explicitly) that the usual access checking rules do not apply for the template argument list for (particularly explicit/full) specializations?
The rule here was changed shortly after C++17 was finished by P0692, replacing what was then [temp.explicit]/14 with what is [temp.class.spec]/10 (for partial specializations) and [temp.spec]/6 (for explicit specializations and instantiations) in C++20. The paper mentions that it was (largely) standardizing existing practice, so it’s not surprising that some compilers (continue to) allow it even in prior language modes.
Access Checking on Specializations
Abstract
This paper attempts to address a long-standing hole in the ability for
developers to specialize templates on their private and protected
nested class-types. It also identifies an implementation divergence
between compilers.
Abstract Code Example
To be clear about what is being discussed, the following code is a
minimal example:
template<class T>
struct trait;
class class_ {
class impl;
};
// Not allowed in standard C++ (impl is private)
template<>
struct trait<class_::impl>;
It is important to note that even though the above specialization of
trait is not allowed according to the standard, it builds with all
compilers that were tested, including various versions of gcc, clang,
icc, and msvc. Already, for the sake of standardizing existing
practice, one might argue that this should be allowed. [...]
Example:
namespace X{
inline namespace Y{
template<typename T>
struct A{
};
}
}
namespace X{
template<typename Z>
A(std::vector<Z>) -> A<Z>;
}
This causes a compile error in Clang 11, which says "Deduction guide must be declared in the same scope as template X::Y::A"
Similar to the template specialization, the deduction guide should also be declared within the same semantic scope as the class template. So why can I specialize the class template outside the inline namespace, but for a deduction guide I can't?
Especially, this causes another problem:
template<typename T>
struct Q{
operator std::vector<T>() {
return {};
}
};
namespace std{
template<typename T>
vector(Q<T>) -> vector<T>;
}
The compiler refuses if I want to define a class template with a conversion to std::vector and declare a deduction guide for it. In this case (for libc++), I have to declare it in namespace std::__1.
Is there some solution or explanation in the CPP standard?
so why I can specialize the class template outside the inline namespace, but for deduction guide I can't?
Because you are allowed to specialize the template. From C++ standard [namespace.def]/7:
Members of an inline namespace can be used in most respects as though they were members of the enclosing namespace. Specifically, the inline namespace and its enclosing namespace are both added to the set of associated namespaces used in argument-dependent lookup whenever one of them is, and a using-directive that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace.
Furthermore, each member of the inline namespace can subsequently be partially specialized, explicitly instantiated, or explicitly specialized as though it were a member of the enclosing namespace
For the deduction guide it needs to be in the same scope as the class template. From the standard [temp.deduct.guide]/3:
[...] A deduction-guide shall be declared in the same scope as the corresponding class template and, for a member class template, with the same access. [...]
Solution would be to explicitly give X::Y scope:
namespace X::inline Y{
template<typename Z>
A(std::vector<Z>) -> A<Z>;
}
The intent behind template specialization is that you can add specializations to a template even if you're not the author of the template. One might do this because they are the author of a type which is being used by this specialization. The C++ standard library's rules forbid adding declarations to the std namespace except for template specializations for precisely this reason.
Deduction guides are not like template specializations. They are considered part of the class template's definition, much like constructors and other member functions. As such, they are expected to be written by the creator of the class, typically immediately following the template class's definition. Given these expectations, it doesn't make sense for deduction guides to exist in a scope other than the scope of the template class definition itself.
Basically, you are not meant to be able to add deduction guides to someone else's class templates.
The very first version of the CTAD proposal, as well as every derivative version thereof, focuses on mapping constructor arguments to class template parameters. What will eventually be known as "deduction guides" were first discussed as "Canonical factory functions". But the text around it is particularly telling:
We suggest a notation to allow constructors to specify their template parameters by either explicitly declaring the signatures for any further needed constructor deductions outside the class
Notice how focused the text is on "constructors". These canonical factory functions are maps between constructors and template arguments. They are considered, conceptually at least, to be constructors of a sort. After all, implicit guides are generated from constructors, so it stands to reason that explicit guides are conceptually equivalent to a class constructor.
Indeed, the prototypical example of why you need explicit deduction guides (that is, why you can't rely entirely on implicit guides) is focused on the constructors of the type. Namely, vector's iterator constructor:
template<typename Iter>
vector(Iter first, Iter last);
A deduction guide is needed to access this constructor because Iter doesn't obviously map to the template parameters of vector<T, A>.
The bottom line is this: explicit deduction guides are built around a class's constructors (though those constructors don't have to exist). They exist to map constructor argument types to class template parameters. If you cannot add a constructor to a class from outside of the class's definition, then it stands to reason that you cannot add an explicit deduction guide from outside of the class's definition either.
Obviously explicit guides are written outside of a template class's definition, but the principle is the same: guides are part of a class's interface.
Implicit conversion via operator Typename does not add a constructor to Typename. It may permit Typename(other_type) to work, but as far as the language standard is concerned, this is a copy/move into Typename. It isn't modifying the definition of Typename.
namespace A{
namespace B{
template<typename T>
struct Test{
};
}
using namespace B;
template<>
struct Test<int>{}; //#1
}
int main(){
}
Consider the above code, GCC complains such code is ill-formed and Clang consider such code is well-formed. The outcome is here. At #1, it's an explicit specialization declaration for class template Test. According to this rule:
temp.expl.spec#2
An explicit specialization may be declared in any scope in which the corresponding primary template may be defined.
The primary template Test can be defined outside namespace B, as long as it obeys the following rule:
namespace.memdef#2
Members of a named namespace can also be defined outside that namespace by explicit qualification ([namespace.qual]) of the name being defined, provided that the entity being defined was already declared in the namespace and the definition appears after the point of declaration in a namespace that encloses the declaration's namespace.
That is we may define the primary template Test like this:
namespace A{
namespace B{
template<typename T>
struct Test;
}
template<typename T>
struct B::Test{ // Test is explicit qualified by B
};
}
So, we're permitted to define explicit specialization at such point. However It's unclear that It is necessary to declare such explicit specialization by using qualified-id? However an additional rule is here:
temp.expl.spec#8
A template explicit specialization is in the scope of the namespace in which the template was defined.
So Clang is wrong? How to interpret such case.
You are correct that the existing wording is more than a little unclear. I won’t try to fathom its meaning as written, if it has one, further than you already have; I consider it all bad enough to need a complete rewrite.
The intent, however, is that this code is well-formed (as Clang says); because the explicit specialization (of a class template) must contain Test<, which might be part of a nested-name-specifier (template<> struct Test<int>::Nested<float> {};), Test is subject to a normal name lookup that follows the using-directive (among many other things). Note that this differs from [class.pre]/3 which forbids using a nested-name-specifier to redeclare a class brought in via a using-declaration. (The rules for explicit specializations and instantiations of function templates are a bit more complicated because the template argument list is optional.)
Within a few weeks, there will be a public update of my linked paper that adjusts the rules for redeclarations along these lines (in [class.pre] in this case).
I can create the following:
using Foo = struct { /*Implementation*/ };
template<class>
using Bar = Foo;
However the following is not allowed:
template<class>
using Bar = struct { /*Implementation*/ };
The error from Clang is more helpful than GCC, and states:
error: '(anonymous struct at file:line:column)' cannot be defined
in a type alias template
Any reasons why the second code example is not allowed?
Note:
Please state any examples for which the second code example (if allowed) may cause problems with the language.
Any citation from the standard is also helpful.
Defining a class or an enumeration in an alias-declaration that is part of a template alias is forbidden by [dcl.typedef]/2:
A typedef-name
can also be introduced by an
alias-declaration.
...
The
defining-type-specifier-seq
of the
defining-type-id
shall not define a class or enumeration if the
alias-declaration
is the declaration of
a
template-declaration.
The latter was introduced as CWG issue 1159 was accepted, as part of FCD N3092.
The comments and proposed resolution of the associated N3092 comment US 74 does provide some rationale as to why this restriction was introduced [emphasis mine]:
Comment (ID) US 74
Comment
An alias-declaration allows a class or enumeration type to be defined
in its type-id (7.1.6p3). However, it's not clear that this is
desirable when the alias-declaration is part of a template alias:
template<typename T> using A =
struct { void f(T) { } };
Proposed resolution
Either prohibit the definition of classes and enumerations in
template aliases, or prohibit the use of template parameters in such
definitions, or add an example illustrating this usage.
Owner & issue
CWG 1159
Disposition
ACCEPTED
Definition of a class or enumeration is now prohibited in a template alias.
It would seem as if no one protested (convincingly enough) to prohibiting the definition of classes and enumerations in template aliases, implying that it's likely that no one was able to give a convincing example illustrating where this would be useful.
Consider this code:
template <int N>
struct X
{
friend void f(X *) {}
};
int main()
{
f((X<0> *)0); // Error?
}
compilers seem to heavily disagree. (MSVC08/10 says no, GCC<4.5 says yes, but 4.5 says no, sun 5.1 says yes, intel 11.1 says yes too but comeau says no (both are EDG)).
According to "C++ Templates - The complete guide":
... it is assumed that a call
involving a lookup for friends in
associated classes actually causes the
class to be instantiated ... Although
this was clearly intended by those who
wrote the C++ standard, it is not
clearly spelled out in the standard.
I couldn't find the relevant section in the standard. Any reference?
Consider this variation:
template <int N>
struct X
{
template <int M>
friend void f(X<M> *) {}
};
template <>
struct X<0>
{
};
int main()
{
X<1>();
f((X<0> *)0); // Error?
}
The key issue here is wether the viable function injected by X<1> should be visible during ADL for X<0>? Are they associated? All compilers mentioned above accept this code, except for Comeau which only accepts it in relaxed mode. Not sure what the standard has to say about this either.
What's your take on that?
The Standard says at 14.7.1/4
A class template specialization is implicitly instantiated if the class type is used in a context that requires a completely-defined object type or if the completeness of the class type affects the semantics of the program; in particular, if an expression whose type is a class template specialization is involved in overload resolution, pointer conversion, pointer to member conversion, the class template specialization is implicitly instantiated (3.2);
Note that Vandervoorde made an issue report here, and the committee found
The standard already specifies that this creates a point of instantiation.
For your second case - you need to consider the associated classes and namespaces of the argument f(X<0>*). These are, since this is a pointer to a class template specialization (note that "template-id" below is not quite correct - C++0x corrected that to use the correct term) and also a pointer to a class (this confusing split was also corrected in C++0x - it lists these two cases in one bullet point).
If T is a template-id, its associated namespaces and classes are the namespace in which the template is
defined; [... lots of noise ...]
If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces in which its associated classes are defined.
So to summary, we have as associated classes are X<0> and the associated namespaces are the global namespace. Now the friend functions that are visible are
Any namespace-scope friend functions declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup
There is no friend function declared in X<0> so the friend function declaration is not visible when looking into the global namespace. Note that X<0> is an entirely different class-type than X<1>. The implicit instantiation of X<1> you do there has no effect on this call - it just adds a non-visible name into the global namespace that refers to a friend function of class X<1>.