📄 chap06.htm
字号:
<font color=#009900>// Template-template argument must </font>
<font color=#009900>// be a class; cannot use typename:</font>
<font color=#0000ff>template</font><<font color=#0000ff>typename</font> T, <font color=#0000ff>template</font><<font color=#0000ff>typename</font>> <font color=#0000ff>class</font> C>
<font color=#0000ff>void</font> print2(C<T>& c) {
copy(c.begin(), c.end(),
ostream_iterator<T>(cout, <font color=#004488>" "</font>));
cout << endl;
}
<font color=#0000ff>int</font> main() {
vector<string> v(5, <font color=#004488>"Yow!"</font>);
print1(v);
print2(v);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><A NAME="_Toc519041989"></A><BR></P></DIV>
<A NAME="Heading166"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Member function templates</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s also possible to make
<B>apply( )</B> a <I>member function
template<A NAME="Index476"></A><A NAME="Index477"></A><A NAME="Index478"></A></I>
of the class. That is, a separate template definition from the class’
template, and yet a member of the class. This may produce a cleaner
syntax:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>dogs.apply(&Gromit::sit);</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is analogous to the act (in Chapter
XX) of bringing ordinary functions inside a
class.</FONT><A NAME="fnB18" HREF="#fn18">[18]</A><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The definition of the
<B>apply( )</B> functions turn out to be cleaner, as well, because they are
members of the container. To accomplish this, a new container is inherited from
one of the existing STL sequence containers and the member function templates
are added to the new type. However, for maximum flexibility we’d like to
be able to use any of the STL sequence containers, and for this to work a
<I>template-template</I> must be used, to tell the compiler that a template
argument is actually a template, itself, and can thus take a type argument and
be instantiated. Here is what it looks like after bringing the
<B>apply( )</B> functions into the new type as member
functions:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:applyMember.h</font>
<font color=#009900>// applySequence.h modified to use </font>
<font color=#009900>// member function templates</font>
<font color=#0000ff>template</font><<font color=#0000ff>class</font> T, <font color=#0000ff>template</font><<font color=#0000ff>typename</font>> <font color=#0000ff>class</font> Seq>
<font color=#0000ff>class</font> SequenceWithApply : <font color=#0000ff>public</font> Seq<T*> {
<font color=#0000ff>public</font>:
<font color=#009900>// 0 arguments, any type of return value:</font>
<font color=#0000ff>template</font><<font color=#0000ff>class</font> R>
<font color=#0000ff>void</font> apply(R (T::*f)()) {
iterator it = begin();
<font color=#0000ff>while</font>(it != end()) {
((*it)->*f)();
it++;
}
}
<font color=#009900>// 1 argument, any type of return value:</font>
<font color=#0000ff>template</font><<font color=#0000ff>class</font> R, <font color=#0000ff>class</font> A>
<font color=#0000ff>void</font> apply(R(T::*f)(A), A a) {
iterator it = begin();
<font color=#0000ff>while</font>(it != end()) {
((*it)->*f)(a);
it++;
}
}
<font color=#009900>// 2 arguments, any type of return value:</font>
<font color=#0000ff>template</font><<font color=#0000ff>class</font> R, <font color=#0000ff>class</font> A1, <font color=#0000ff>class</font> A2>
<font color=#0000ff>void</font> apply(R(T::*f)(A1, A2),
A1 a1, A2 a2) {
iterator it = begin();
<font color=#0000ff>while</font>(it != end()) {
((*it)->*f)(a1, a2);
it++;
}
}
}; <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because they are members, the
<B>apply( )</B> functions don’t need as many arguments, and the
<B>iterator</B> class doesn’t need to be qualified. Also,
<B>begin( )</B> and <B>end( )</B> are now member functions of the new
type and so look cleaner as well. However, the basic code is still the
same.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see how the function calls are
also simpler for the client programmer:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:applyGromit2.cpp</font>
<font color=#009900>// Test applyMember.h</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#009900>//{-g++295}</font>
<font color=#009900>//{-g++3}</font>
<font color=#009900>//{-msc}</font>
#include <font color=#004488>"Gromit.h"</font>
#include <font color=#004488>"applyMember.h"</font>
#include <vector>
#include <iostream>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>int</font> main() {
SequenceWithApply<Gromit, vector> dogs;
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < 5; i++)
dogs.push_back(<font color=#0000ff>new</font> Gromit(i));
dogs.apply(&Gromit::speak, 1);
dogs.apply(&Gromit::eat, 2.0f);
dogs.apply(&Gromit::sleep, 'z', 3.0);
dogs.apply(&Gromit::sit);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Conceptually, it reads more sensibly to
say that you’re calling <B>apply( )</B> for the <B>dogs</B>
container.</FONT><A NAME="_Toc519041990"></A><BR></P></DIV>
<A NAME="Heading167"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Why virtual member template functions are
disallowed<A NAME="_Toc519041991"></A></H3></FONT>
<A NAME="Heading168"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Nested template
classes<A NAME="_Toc312374093"></A><A NAME="_Toc519041992"></A></H3></FONT>
<A NAME="Heading169"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Template specializations<A NAME="_Toc519041993"></A></H2></FONT>
<A NAME="Heading170"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Full specialization<A NAME="_Toc519041994"></A></H3></FONT>
<A NAME="Heading171"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Partial Specialization<A NAME="_Toc519041995"></A></H3></FONT>
<A NAME="Heading172"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
A practical example<BR><A NAME="Index479"></A><A NAME="Index480"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There’s nothing to prevent you from
using a class template in any way you’d use an ordinary class. For
example, you can easily inherit from a template, and you can create a new
template that instantiates and inherits from an existing template. If the
<B>vector </B>class does everything you want, but you’d also like it to
sort itself, you can easily reuse the code and add value to it:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:Sorted.h</font>
<font color=#009900>// Template specialization</font>
#ifndef SORTED_H
#define SORTED_H
#include <vector>
<font color=#0000ff>template</font><<font color=#0000ff>class</font> T>
<font color=#0000ff>class</font> Sorted : <font color=#0000ff>public</font> std::vector<T> {
<font color=#0000ff>public</font>:
<font color=#0000ff>void</font> sort();
};
<font color=#0000ff>template</font><<font color=#0000ff>class</font> T>
<font color=#0000ff>void</font> Sorted<T>::sort() { <font color=#009900>// A bubble sort</font>
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = size(); i > 0; i--)
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> j = 1; j < i; j++)
<font color=#0000ff>if</font>(at(j-1) > at(j)) {
<font color=#009900>// Swap the two elements:</font>
T t = at(j-1);
at(j-1) = at(j);
at(j) = t;
}
}
<font color=#009900>// Partial specialization for pointers:</font>
<font color=#0000ff>template</font><<font color=#0000ff>class</font> T>
<font color=#0000ff>class</font> Sorted<T*> : <font color=#0000ff>public</font> std::vector<T*> {
<font color=#0000ff>public</font>:
<font color=#0000ff>void</font> sort();
};
<font color=#0000ff>template</font><<font color=#0000ff>class</font> T>
<font color=#0000ff>void</font> Sorted<T*>::sort() {
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = size(); i > 0; i--)
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> j = 1; j < i; j++)
<font color=#0000ff>if</font>(*at(j-1) > *at(j)) {
<font color=#009900>// Swap the two elements:</font>
T* t = at(j-1);
at(j-1) = at(j);
at(j) = t;
}
}
<font color=#009900>// Full specialization for char*:</font>
<font color=#0000ff>template</font><>
<font color=#0000ff>void</font> Sorted<<font color=#0000ff>char</font>*>::sort() {
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = size(); i > 0; i--)
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> j = 1; j < i; j++)
<font color=#0000ff>if</font>(strcmp(at(j-1), at(j)) > 0) {
<font color=#009900>// Swap the two elements:</font>
<font color=#0000ff>char</font>* t = at(j-1);
at(j-1) = at(j);
at(j) = t;
}
}
#endif <font color=#009900>// SORTED_H ///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>Sorted</B> template imposes a
restriction on all classes it is instantiated for: They must contain a
<B>></B> operator. In <B>SString</B> this is added explicitly, but in
<B>Integer</B> the automatic type conversion <B>operator int</B> provides a path
to the built-in <B>></B> operator. When a template
<A NAME="Index481"></A>provides more functionality for you, the trade-off is
usually that it puts more requirements on your class. Sometimes you’ll
have to inherit the contained class to add the required functionality. Notice
the value of using an overloaded operator here – the <B>Integer</B> class
can rely on its underlying implementation to provide the
functionality.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The default <B>Sorted</B> template only
works with objects (including objects of built-in types). However, it
won’t sort pointers to objects so the partial specialization is necessary.
Even then, the code generated by the partial specialization won’t sort an
array of <B>char*</B>. To solve this, the full specialization compares the
<B>char*</B> elements using <B>strcmp( )</B> to produce the proper
behavior.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s a test for <B>Sorted.h</B>
that uses the unique random number generator introduced earlier in the
chapter:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:Sorted.cpp</font>
<font color=#009900>// Testing template specialization</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#009900>//{-g++295}</font>
<font color=#009900>//{-msc}</font>
#include <font color=#004488>"Sorted.h"</font>
#include <font color=#004488>"Urand.h"</font>
#include <font color=#004488>"..</font><font color=#004488>/arraySize.h"</font>
#include <iostream>
#include <string>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>char</font>* words[] = {
<font color=#004488>"is"</font>, <font color=#004488>"running"</font>, <font color=#004488>"big"</font>, <font color=#004488>"dog"</font>, <font color=#004488>"a"</font>,
};
<font color=#0000ff>char</font>* words2[] = {
<font color=#004488>"this"</font>, <font color=#004488>"that"</font>, <font color=#004488>"theother"</font>,
};
<font color=#0000ff>int</font> main() {
Sorted<<font color=#0000ff>int</font>> is;
Urand<47> rand;
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < 15; i++)
is.push_back(rand());
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> l = 0; l < is.size(); l++)
cout << is[l] << ' ';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -