While writing a small template metaprogramming library for personal use, I came across an interesting problem.
Since I was reusing a few partial specializations for some metafunctions, I decided I would put them under a common template class and use tags along with nested partial specialization to provide the differences in behaviour.
The problem is I am getting nonsensical (to me) results. Here is a minimal example that showcases what I am trying to do:
#include <iostream>
#include <cxxabi.h>
#include <typeinfo>
template <typename T>
const char * type_name()
{
return abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);
}
template <typename... Args>
struct vargs {};
namespace details
{
template <typename K>
struct outer
{
template <typename Arg>
struct inner
{
using result = Arg;
};
};
}
struct tag {};
namespace details
{
template <>
template <typename Arg, typename... Args>
struct outer<tag>::inner<vargs<Arg, Args...>>
{
using result = typename outer<tag>::inner<Arg>::result;
};
}
template <typename T>
using test_t = typename details::outer<tag>::inner<T>::result;
int main()
{
using t = test_t<vargs<char, int>>;
std::cout << type_name<t>() << '\n';
return 0;
}
I am getting vargs<char, int> as output when using the 5.1.0 version of gcc and tag when using the 3.6.0 version of clang. My intention was for the above piece of code to print char so I am pretty baffled by these results.
Is the above piece of code legal or does it exhibit undefined behavior?
If it's legal what is the expected behavior according to the standard?
Your code is correct; out-of-class implicitly instantiated class template member class template partial specializations are intended to be allowed by the Standard, as long as they are defined early enough.
First, let's try for a minimal example - noting by the way that there's nothing here that requires C++11:
template<class T> struct A {
template<class T2> struct B { };
};
// implicitly instantiated class template member class template partial specialization
template<> template<class T2>
struct A<short>::B<T2*> { };
A<short>::B<int*> absip; // uses partial specialization?
As noted elsewhere MSVC and ICC use the partial specialization as expected; clang selects the partial specialization but messes up its type parameters, aliasing T2 to short instead of int; and gcc ignores the partial specialization entirely.
Why out-of-class implicitly instantiated class template member class template partial specialization is allowed
Put simply, none of the language that permits other forms of class template member class template definitions excludes out-of-class implicitly instantiated class template member class template partial specialization. In [temp.mem], we have:
1 - A template can be declared within a class or class template; such a template is called a member template. A
member template can be defined within or outside its class definition or class template definition. [...]
A class template partial specialization is a template declaration ([temp.class.spec]/1). In the same paragraph, there is an example of out-of-class nonspecialized class template member class template partial specialization ([temp.class.spec]/5):
template<class T> struct A {
struct C {
template<class T2> struct B { };
};
};
// partial specialization of A<T>::C::B<T2>
template<class T> template<class T2>
struct A<T>::C::B<T2*> { };
A<short>::C::B<int*> absip; // uses partial specialization
There is nothing here to indicate that the enclosing scope cannot be an implicit specialization of the enclosing class template.
Similarly, there are examples of in-class class template member class template partial specialization and out-of-class implicitly instantiated class template member class template full specialization ([temp.class.spec.mfunc]/2):
template<class T> struct A {
template<class T2> struct B {}; // #1
template<class T2> struct B<T2*> {}; // #2
};
template<> template<class T2> struct A<short>::B {}; // #3
A<char>::B<int*> abcip; // uses #2
A<short>::B<int*> absip; // uses #3
A<char>::B<int> abci; // uses #1
(clang (as of 3.7.0-svn235195) gets the second example wrong; it selects #2 instead of #3 for absip.)
While this does not explicitly mention out-of-class implicitly instantiated class template member class template partial specialization, it does not exclude it either; the reason it isn't here is that it's irrelevant for the particular point being made, which is about which primary template or partial template specializations are considered for a particular specialization.
Per [temp.class.spec]:
6 - [...] when the primary
template name is used, any previously-declared partial specializations of the primary template are also
considered.
In the above minimal example, A<short>::B<T2*> is a partial specialization of the primary template A<short>::B and so should be considered.
Why it might not be allowed
In other discussion we've seen mention that implicit instantiation (of the enclosing class template) could result in implicit instantiation of the definition of the primary template specialization to take place, resulting in an ill-formed program NDR i.e. UB; [templ.expl.spec]:
6 - If a template, a member template or a member of a class template is explicitly specialized then that specialization
shall be declared before the first use of that specialization that would cause an implicit instantiation
to take place, in every translation unit in which such a use occurs; no diagnostic is required. [...]
However, here the class template member class template is not used before it is instantiated.
What other people think
In DR1755 (active), the example given is:
template<typename A> struct X { template<typename B> struct Y; };
template struct X<int>;
template<typename A> template<typename B> struct X<A>::Y<B*> { int n; };
int k = X<int>::Y<int*>().n;
This is considered problematic only from the point of view of the existence of the second line instantiating the enclosing class. There was no suggestion from the submitter (Richard Smith) or from CWG that this might be invalid even in the absence of the second line.
In n4090, the example given is:
template<class T> struct A {
template<class U> struct B {int i; }; // #0
template<> struct B<float**> {int i2; }; // #1
// ...
};
// ...
template<> template<class U> // #6
struct A<char>::B<U*>{ int m; };
// ...
int a2 = A<char>::B<float**>{}.m; // Use #6 Not #1
Here the question raised is of precedence between an in-class class template member class template full specialization and an out-of-class class template instantiation member class template partial specialization; there is no suggestion that #6 would not be considered at all.
Related
I have read What does template's implicit specialization mean? and its answers, but I am still not satisfied that I understand this part of Partial template specialization from cppreference.com:
If the primary member template is explicitly (fully) specialized for a given (implicit) specialization of the enclosing class template, the partial specializations of the member template are ignored for this specialization of the enclosing class template....
template<class T> struct A { //enclosing class template
template<class T2>
struct B {}; //primary member template
template<class T2>
struct B<T2*> {}; // partial specialization of member template
};
template<>
template<class T2>
struct A<short>::B {}; // full specialization of primary member template
// (will ignore the partial)
A<char>::B<int*> abcip; // uses partial specialization T2=int
A<short>::B<int*> absip; // uses full specialization of the primary (ignores partial)
A<char>::B<int> abci; // uses primary
Questions:
The comments say that the line template<> template<classT2> struct A<short>::B {}; is a "full specialization of primary member template". The primary member template is identified in the comments as the structure B. How can the line be a specialization of B when it is A that is being specialized by the substitution of short for class T?
How can the line be a "full" specialization of B when the template parameter T2 is left unspecified?
The comments and the accompanying text indicate that "explicit specialization" and "full specialization" are synonymous terms. If the line of code quoted above is the explicit specialization of B, where is the implicit specialization of A?
A member of a class template can be explicitly specialized even if it is not a template:
template<int I>
struct X {
int f() {return I;}
void g() {}
};
template<>
int X<0>::f() {return -1;}
This is equivalent to specializing the whole class template but with other members duplicated from the relevant primary template or partial specialization:
template<>
struct X<0> {
int f() {return -1;}
void g() {}
};
(Recall that such a spelled-out specialization is under no obligation to declare g at all or as a function.) Since you don’t actually write this, it’s still considered to be an implicit instantiation of X as a whole with the given alteration.
This is why your specialization of A<T>::B provides the template argument for A and not for B; it is replacing the template A<T>::B with another template (that happens to have the same template parameter list). I would say the word “primary” is misleading here: it is the whole template that is replaced, which is why the partial specialization is ignored for A<short> thereafter.
I can use std::experimental::is_detected to check whether chips1_t can be instantiated with float* (which it can't) as shown in the static_assert below:
#include <experimental/type_traits>
#include <type_traits>
template <typename, typename = void>
struct chips1;
template <typename T>
struct chips1<T*,std::enable_if_t<std::is_same<T,int>::value>> {
using type = int;
};
template <typename T> using chips1_t = typename chips1<T>::type;
static_assert(!std::experimental::is_detected<chips1_t,float*>::value,"");
If I then try a check using chips2_t, a similar static_assert, shown below, will produce a compilation error; regarding the missing type member. Can anyone tell me why?
struct Base {};
template <typename>
struct chips2;
template <typename T>
struct chips2<T*> : std::enable_if_t<std::is_same<T,int>::value,Base> {
using type = int;
};
template <typename T> using chips2_t = typename chips2<T>::type;
static_assert(!std::experimental::is_detected<chips2_t,float*>::value,"");
In the second case, the base class is not part of the template type, so it's not considered during SFINAE (It's a dependent base class)
Instantiating
template <typename T>
struct chips2<T*>
Succeeds, and then compilation fails because it derives from std::enable_if_t<std::is_same<T,int>::value,Base> which becomes an ill-formed expression.
You cannot specialize templates by their base class*.
For example, you couldn't have two competing specializations like:
template <typename T>
struct chips2<T*> : std::enable_if_t<std::is_same<T,int>::value,Base> {
using type = int;
};
template <typename T>
struct chips2<T*> : std::enable_if_t<!std::is_same<T,int>::value,Base> {
using type = float;
};
They would be considered identical from a specialization standpoint (i.e., they are identical from a naming standpoint, so the compiler will consider the second one to be a redeclaration of the first one). The only part that matters is everything up to the colon :
In the first case, your enable_if is directly part of the template specialization, so SFINAE works properly.
Another note
In the second one, you've effectively made it impossible to instantiate chips2 with anything other than int*, see the following example:
struct Base {};
template <typename>
struct chips2
{};
template <typename T>
struct chips2<T*>
: std::enable_if_t<std::is_same<T,int>::value, Base>
{};
int main(){
chips2<float*> c;
}
You might be inclined to think that SFINAE will choose the base class template for c, but in reality it chooses the specialization chips2<T*> because of what I said above, and compilation fails.
*Relevant standardese at [temp.spec]
the name of the class that is explicitly specialized shall be a simple-template-id.
Where a simple-template-id is of the form:
template-name < template-argument-listopt >
e.g. chips2<T*>. Note that there is no option to also include the class it derives from
Here on stackoverflow I've found several comments (see for example the comment by jrok on this question) stating that partial specializations of class member templates are allowed at non-namespace scope (in contrast to explicit specializations) like in the example below:
class A {
template <class T, class U>
class B {};
template <class U>
class B<void, U> {};
};
Also, this example compiles just fine with both gcc and clang. However in the c++03 standard text I can only find 14.5.4 [temp.class.spec] §6 (or 14.5.5 §5 in c++11) about this issue:
A class template partial specialization may be declared or redeclared in any namespace scope in which its definition may be defined (14.5.1 and 14.5.2).
Along with the following example:
template<class T> struct A {
class C {
template<class T2> struct B { };
};
};
// partial specialization of A<T>::C::B<T2>
template<class T> template<class T2>
struct A<T>::C::B<T2*> { };
So, how about class template partial specializations at non-namespace scope? Are they allowed by the standard? And where can I find the relevant text?
Specifically, is my example valid (and would it still be valid if the enclosing class would be a template)? If not, are current compilers wrong to compile my example as stated above?
This seems to be a bit confusing, as I agree the paragraph you quoted seems to disallow partial specializations inside class definitions. However, there's [temp.class.spec.mfunc]/2:
If a member template of a class template is partially specialized, the
member template partial specializations are member templates of the
enclosing class template; [...] [Example:
template<class T> struct A {
template<class T2> struct B {}; // #1
template<class T2> struct B<T2*> {}; // #2
};
template<> template<class T2> struct A<short>::B {}; // #3
A<char>::B<int*> abcip; // uses #2
A<short>::B<int*> absip; // uses #3
A<char>::B<int> abci; // uses #1
— end example ]
IMHO this isn't very explicit; it doesn't allow partial specialization inside the class definition but rather (to me, seems to) specifies how member template specializations are treated.
Also see the Core Language Issue 708 and EWG Issue 41.
This is from the C++ Standard Library xutility header that ships with VS2012.
template<class _Elem1,
class _Elem2>
struct _Ptr_cat_helper
{ // determines pointer category, nonscalar by default
typedef _Nonscalar_ptr_iterator_tag type;
};
template<class _Elem>
struct _Ptr_cat_helper<_Elem, _Elem>
{ // determines pointer category, common type
typedef typename _If<is_scalar<_Elem>::value,
_Scalar_ptr_iterator_tag,
_Nonscalar_ptr_iterator_tag>::type type;
};
Specifically what is the nature of the second _Ptr_cat_helper declaration? The angle brackets after the declarator _Ptr_cat_helper make it look like a specialization. But instead of specifying full or partial types by which to specialize the template it instead just repeats the template argument multiple times.
I don't think I've seen that before. What is it?
UPDATE
We are all clear that the specialization applies to an instantiation of the template where both template arguments are of the same type, but I'm not clear on whether this constitutes a full or a partial specialization, or why.
I thought a specialization was a full specialization when all the template arguments are either explicitly supplied or are supplied by default arguments, and are used exactly as supplied to instantiate the template, and that conversely a specialization was partial either, if not all the template parameters were required due to the specialization supplying one or more (but not all) of them, and/or if the template arguments were used in a form that was modified by the specialization pattern. E.g.
A specialization that is partial because the specialization is supplying at least one, but not all, of the template arguments.
template<typename T, typename U>
class G { public: T Foo(T a, U b){ return a + b; }};
template<typename T>
class G<T, bool> { public: T Foo(T a, bool b){ return b ? ++a : a; }};
A specialization that is partial because the specialization is causing the supplied template argument to be used only partially.
template<typename T>
class F { public: T Foo(T a){ return ++a; }};
template<typename T>
class F<T*> { public: T Foo(T* a){ return ++*a; }};
In this second example if the template were instantiated using A<char*> then T within the template would actually be of type char, i.e. the template argument as supplied is used only partially due to the application of the specialization pattern.
If that is correct then wouldn't that make the template in the original question a full specialization rather than a partial specialization, and if that is not so then where is my misunderstanding?
It is a partial class template specialization for the case when the same type is passed for both parameters.
Maybe this will be easier to read:
template<typename T, typename U>
struct is_same : std::false_type {};
template<typename T>
struct is_same<T,T> : std::true_type {};
EDIT:
When in doubt whether a specialization is an explicit (full) specialization or a partial specialization, you can refer to the standard which is pretty clear on this matter:
n3337, 14.7.3./1
An explicit specialization of any of the following:
[...]
can be declared by a declaration introduced by template<>; that is:
explicit-specialization:
template < > declaration
and n3337, 14.5.5/1
A primary class template declaration is one in which the class
template name is an identifier. 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. [...]
Where simple-template-id is defined in the grammar like this:
simple-template-id:
template-name < template-argument-list opt >
template-name
identifier
So, wherever there's template<>, it's a full specialization, anything else is a partial specialization.
You can also think about it this way: Full template specialization specializes for exactly one possible instantiation of the primary template. Anything else is a partial specialization. Example in your question is a partial specialization because while it limits the arguments to be of the same type, it still allows for indifinitely many distinct arguments the template can be instantiated with.
A specialization like this, for example
template<>
vector<bool> { /* ... */ };
is a full specialization because it kicks in when the type is bool and only bool.
Hope that helps.
And just a note I feel it's worth mentioning. I guess you already know - function templates can't be partialy specialized. While this
template<typename T>
void foo(T);
template<typename T>
void foo(T*);
might looks like a partial specialization of foo for pointers on the first glance, it is not - it's an overload.
You mention specifying "full or partial types" when performing specialization of a template, which suggests that you are aware of such language feature as partial specialization of class templates.
Partial specialization has rather extensive functionality. It is not limited to simply specifying concrete arguments for some of the template parameters. It also allows defining a dedicated version of template for a certain groups of argument types, based on their cv-qualifications or levels of indirection, as in the following example
template <typename A, typename B> struct S {};
// Main template
template <typename A, typename B> struct S<A *, B *> {};
// Specialization for two pointer types
template <typename A, typename B> struct S<const A, volatile B> {};
// Specialization for const-qualified type `A` and volatile-qualified type `B`
And it also covers specializations based on whether some template arguments are identical or different
template <typename A> struct S<A, A> {};
// Specialization for two identical arguments
template <typename A> struct S<A, A *> {};
// Specialization for when the second type is a pointer to the first one
As another, rather curios example, partial specialization of a multi-argument template can be used to fully override the main template
template <typename A, typename B> struct S<B, A> {};
// Specialization for all arguments
Now, returning to your code sample, partial specialization for two identical arguments is exactly what is used in the code you posted.
What is the difference between specialization and instantiation in context of C++ templates. From what I have read so far the following is what I have understood about specialization and instantiation.
template <typename T>
struct Struct
{
T x;
};
template<>
struct Struct <int> //specialization
{
//code
};
int main()
{
Struct <int> s; //specialized version comes into play
Struct <float> r; // Struct <float> is instantiated by the compiler as shown below
}
Instantiation of Struct <float> by the compiler
template <typename T=float>
struct Struct
{
float x;
}
Is my understanding of template instantiation and specialization correct?
(Implicit) Instantiation
This is what you refer to as instantiation (as mentioned in the Question)
Explicit Instantiation
This is when you tell the compiler to instantiate the template with given types, like this:
template Struct<char>; // used to control the PLACE where the template is inst-ed
(Explicit) Specialization
This is what you refer to as specialization (as mentioned in the Question)
Partial Specialization
This is when you give an alternative definition to a template for a subset of types, like this:
template<class T> class Struct<T*> {...} // partial specialization for pointers
What is the difference between specialization and instantiation in context of C++ templates?
Normally (no specializations present) the compiler will create instantiations of a template when they are used, by substituting actual template parameters (int in your example) for the formal template parameters (T) and then compile the resulting code.
If a specialization is present, then for the (set of) special template parameter(s) specified by that specialization, that specialization's implementation is to be used instead of what the compiler would create.
Overview
Specialization: The class, function or class member you get when substituting template arguments into the template parameters of a class template or function template.
Instantiation: The act of creating a specialization out of a template or class template member. The specialization can be created out of a partial specialization, class template member or out of a primary class or function template.
An explicit specialization is one that defines the class, function or member explicitly, without an instantiation.
A template specialization actually changes the behaviour of the template for a specific type. eg convert to a string:
template<typename T> std::string convertToString( const T& t )
{
std::ostringstream oss;
oss << t;
return oss.str();
}
Let's specialise that though when our type is already a std::string as it is pointless going through ostringstream
template<> std::string convertToString( const std::string & t )
{
return t;
}
You can specialise for classes too.
Now instantiation: this is done to allow you to move the compilation for certain types into one compilation unit. This can save you both compilation time and sometimes code-bloat too.
Let's say we make the above into a class called StringConvert rather than a function.
template<typename T>
class StringConvert
{
public:
// 4 static functions to convert from T to string, string to T,
// T to wstring and wstring to T using streams
};
We will convert a lot of integers to strings so we can instantiate it: Put this inside one header
extern template class StringConvert<int>;
Put this inside one compilation unit:
template class StringConvert<int>;
Note that the above can also be done (without the extern in the header) with functions that are actually not implemented inline. One of your compilation units will implement them. However then your template is limited only to instantiated types. Sometimes done when the template has a virtual destructor.
In c++ 11.
instantiation:
Instantiate the template with given template arguments
template <typename T>
struct test{ T m; };
template test<int>;//explicit instantiation
which result in a definition of a struct with a identifier test<int>
test<int> a;//implicit instantiation
if template <typename T> struct test has been instantiated with argument T = int before(explicit or implicit), then it's just a struct instantiation. Otherwise it will instantiate template <typename T> struct test with argument T = int first implicitly and then instantiate an instance of struct test<int>
specialization:
a specialization is still a template, you still need instantiation to get the real code.
template <typename T>
struct test{ T m; };
template <> struct test<int>{ int newM; } //specialization
The most useful of template specialization is probably that you can create different templates for different template arguments which means you can have different definitions of class or function for different template arguments.
template<> struct test<char>{ int cm; }//specialization for char
test<char> a;
a.cm = 1;
template<> struct test<long> { int lm; }//specialization for long
test<long> a;
a.lm = 1;
In addition to these full template specializations above, there(only class template) exits partial template specialization also.
template<typename T>
struct test {};
template <typename T> struct test<const T>{};//partial specialization for const T
template <typename A, typename B>
struct test {};
template <typename B> struct test<int, B>{};//partial specialization for A = int
A specialized template is no longer just a template. Instead, it is either an actual class or an actual function.
A specialization is from either an instantiation or an explicit specialization, cf 14.7.4 below.
An instantiation is based on a primary template definition. A sample implicit class template instantiation,
template<typename T>
class foo {}
foo<int> foo_int_object;
A sample explicit class template instantiation,
template class foo<double>;
An explicit specialization has a different definition from it's primary template.
template<>
class foo<bool> {}
// extract from standard
14 Templates
14.7 Template instantiation and specialization
4 An instantiated template specialization can be either implicitly instantiated (14.7.1) for a given argument
list or be explicitly instantiated (14.7.2). A specialization is a class, function, or class member that is either
instantiated or explicitly specialized (14.7.3).