TypeName the template of the template template for the template member template template, the argument matches
This article continues to delve into the basics of templates, covers content as follows: Another use of Keyword TypeName defines member functions and nested classes as template parameters for template templates (template template parameters) TypeName
In the process of standardizing C + + , keyword TypeName is introduced to illustrate that identifiers within template type parameters (associated type, which are common in STL) can also be a type:
Template<typename t>
class MyClass
{
typename t::subtype* ptr;
}
In the above program, the second typename is used to illustrate that subtype is defined as a type within the class T, that is associated type, and thus, PTR is a pointer to the T::subtype type. If you do not use Typename,t::subtype, you will be prioritized as a static member of T, which is a specific variable or object, so the following expression:
T::subtype * PTR;
will be seen as the product of the static member subtype and PTR of class T.
In general, you must use the TypeName keyword when you want to use the associated types of a type template parameter in the form of a type, that is, a type that is defined within it.
Let's consider a typical scenario for TypeName, which is to access the iterator (const_iterator) of the STL container in the template code, as the associated type for each container class.
Template<typename coll>
void Printcoll (ostream& os, const coll& COLL)
{
typename COLL:: Const_iterator Pos;
TypeName Coll::const_iterator End (Coll.end ());
for (pos = Coll.begin (), POS!= end; ++pos)
os << *pos << "";
Os << Endl;
}
. Template
We illustrate this grammatical feature in a small example:
Although the Bitset<> template class provides a simple interface with string interaction (conversion), such as the constructor for the string parameter type of bitset<> and a very handy to_string member function, both are member templates. Cannot be used like a normal member function. For more detailed information on this part, please see
template<size_t n>
string to_string (const bitset<n>& B)
{return
b.template
to_ String<char, Char_traits<char>,
allocator<char> > ();
}
The purpose of this function is to encapsulate the value of the B.to_string member function template,
string s = B.template To_string<char, char_traits<char>,
allocator<char> > ();
With the aid of the above auxiliary function
string s = to_string<6> (b);
In the old-fashioned C + + compiler, the. Template cannot be omitted, without this. Template, the compiler cannot determine whether the following < is less than or the starting symbol for the template argument list. member Templates
A member of a class can also be a template. Both nested classes and member functions can be used as templates.
We had a discussion before
Template<typename t>
class Stack
{public
:
void push (const t& x)
{elems.push_back (x);} //End into
void Pop () {elems.pop_back ();} The end of the end, to achieve a filo mechanism
t& top () {return elems.back ();}
Const t& Top () const {return elems.back ();}
BOOL Empty () const {return elems.empty ();}
Private:
std::d eque<t> elems;
Typically, stacks can be assigned to each other only when the element type is exactly the same, in other words, stacks of different types cannot be assigned, even if there is an implicit type grab between the two types (element), such as:
Stack<int> is1, Is2;
Stack<double> DS3;
//...
Is1 = Is2; OK: With the same type of stack
is3 = is1; ERROR: Different types of stacks on both sides
Class China default assignment operator requirement = Both sides have the same element type. By defining an assignment operator in the form of a template, two stacks of different element types can be assigned to each other.
Template<typename t>
class Stack
{public
:
//...
Template<typename t2>
stack<t>& operator= (const stack<t2>& RHS);
The return value does not contain the const attribute, guaranteeing ' S1 = S2 = S3 '
}
The implementation of the new assignment operator is roughly as follows:
Template<typename t>
template<typename t2>
stack<t>& stack<t>::operator= ( Const stack<t2>& RHS)
{
if (void*) This = = (void*) &rhs) return
*this; Identity test, Identity testing
stack<t2> tmp (RHS);
Elems.clear ();
while (!tmp.empty ())
{
elems.push_back (tmp.top ());
Tmp.pop ();
}
return *this;
}
The new type check occurs in:
Elems.push_back (Tmp.top ());
Similarly, in an implementation, you can implement an internal container type as a template parameter:
Cont types of containers must support STACK<, > class template body operations or interfaces
//such as Push_back, Pop_back, back, empty template<typename
T, TypeName CONT = std::d eque<t> >
class Stack
{public
:
void push (const t& x) {Elems.push_ Back (x);}
void Pop () {elems.pop_back ();}
t& Top () {return elems.back ();}
Const t& Top () const {return elems.back ();}
BOOL Empty () const {return elems.empty ();}
Template<typename T2, TypeName cont2>
stack<t, cont>& operator= (const STACK<T2, cont2> & RHS);
Private:
CONT elems;
}
Template<typename T, TypeName cont>
template<typename T2, TypeName cont2> stack<t
, cont> & Stack<t, cont>::operator= (const STACK<T2, cont2>& rhs)
{
if (void*) This = = (vois*) &RHS) return
*this;
STACK<T2, cont2> tmp (RHS);
Elems.clear ();
while (!tmp.empty ())
{
elems.push_back (tmp.top ());
Tmp.pop ();
}
return *this;
}
Template for templates
This is a colloquial version of the argument, precisely, to template as another template parameter, is not more around,:-D. We continue to use the Stack template class as an example to explore the need for template templates, and then, to a large extent, C + + template technology Evolution clues.
The second template parameter is the instantiation of a template
//must be passed, Stack<int, deque<int> >
//stack<int, queue<int> >
Template<typename T, typename CONT = std::d eque<t> >
class stack{...};
Stack<int, std::vector<int> > IntStack;
We see that there is redundancy in the above code (it suddenly comes to mind that a template class can be called a class with parameters, just like a function.) , that is, the first template parameter is the parameter of the second template parameter, and it is possible to have parameter passing errors, such as Stack<int, Deque<double> >. We suspect that there are no redundant declarations as follows:
Stack<int, std::vector> IntStack;
To support this syntax feature:
Template<typename T, Template<typename elem>
class CONT = std::d eque >
class Stack
{
Public:
void push (const t& x);
void Pop ();
t& top ();
Const t& Top () const;
BOOL Empty () const {return elems.empty ();}
Private:
cont<t> elems;
}
This differs from the preceding code in that the second parameter is instantiated from a class template into a class template, and the default value naturally goes from std::d eque<t> to std::d eque, when used, the second argument must be a class template, and instantiated by the type passed in by the first template parameter (this example is more specific and can typically instantiate template parameters for templates within the class template with any allowed type):
Cont<t> Elems;
Prior to the basics of the template, we have said that as a template parameter declaration, you can often use TypeName to replace the keyword class, however, the above Cont is to define a template class, must be decorated with the keyword class.
Template<typename T, Template<typename elem>
class CONT = std::d eque>
class Stack {...}; Correct
template<typename T, Template<typename elem>
typename CONT = std::d eque>
class Stack {... } //Error
Just as we can omit the formal parameter name in the declaration of the class member function or global function of the. hpp file. Preserving formal parameter types, we can also use the parameters of the template as template parameters here, also known as Elem, because the body of the function that is not involved, or that is involved in the function body, is not used in the function body, and is visible in the real memory model, the operating certificate of deposit Meta, rather than the formal parameter name (presumably in a structure called a symbol table), which can be found in the compiler principle or the C + + memory model-related details.
Template<typename T, template<typename>
class CONT = std::d eque>
class Stack
In addition, you must modify the declaration of the member function of the member function, specifying the second template parameter as the template parameter:
Template<typename T, template<typename> class cont>
void Stack<t, Cont>::p ush (const t& x) c11/>{
elems.push_back (x);
}
Template's template's argument matches
If you use the new version of stack without modification or even call the client code without instantiating it, you will get an error message at compile time, the default std::d eque and template parameter cont do not match. The problem is that template arguments (such as std::d eque In this example) are also a template with parameters (std::d eque as follows, and a template with two template parameters):
Template<class _ty,
class _alloc = allocator<_ty> >
class deque
It replaces the template parameters here (also that is, the cont here), the template parameter of the template is a template with parameter B (also the template of this example) the matching procedure requires that parameter A and parameter B must match exactly, and here, as previously shown, does not match exactly, One is a class template with two template parameters, and one with only one template parameter. At this point we can rewrite the declaration of the class to match the number of arguments.
Template<typename T, Template<typename ELEM, template ALLOC = std::allocator<elem>
Class CONT = std:: Deque>
class Stack
{
private:
cont<t> elems; Cont<t, allocator<t> >
/deque<t, allocator<t> >
}
Also at this point, alloc can be omitted from writing, because it is not used in the implementation. Now we can write the full version of the stack's statement and definition:
Template<typename T, Template<typename ELEM, TypeName = std::alloctor<elem> >
Class Cont> class Stack {public:void push (const t&);
void Pop ();
t& top ();
Const t& () const;
BOOL empty () const; Member Templates Template<typename T2, Template<typename ELEM2, Typname alloc=std::allocator< Elem2> > class cont2> stack<t, cont>& operator= (const STACK<T2, Cont2>
;&); Private:cont<t> Elems; This completes the instantiation of Template<typename T, Template<typename, typename> class cont> void stack<
T, Cont>::p ush (const t& x) {elems.push_back (x);} Template<typename T, Template<typename, typename> class cont> Template<typenam E T2, Template<typename, typename> class cont2> stack<t, CONT>& stack<t, cont>::operator= (const STACK<T2, cont2>& RHS) {if ((void*) This = = (void*) &rhs)
Identity Test return *this;
STACK<T2, cont2> tmp (RHS);
Elems.clear ();
while (!tmp.empty ()) {push (Tmp.top ());
Tmp.pop ();
return *this;
}
About the two-version top () member function, one is a const return constant reference, one that is not decorated with these constants, essentially because the back () function of the Cont container inside the Stack template class (or STL is a requirement or specification for all container classes). Returns two versions of the internal elements of the return container, one mutable sequence, and the other is nonmutable sequence)
Deque, internal variable and immutable two versions of the back () member function
typedef typename _MYBASE::REFERENCE Reference;
typedef typename _mybase::const_reference Const_reference;
Reference back ()
{ //return last element of mutable sequence return
(* (End ()-1));
}
Const_reference back () const
{ //return last element of nonmutable sequence return
(* (End ()-1));
}