📄 chap06.htm
字号:
<font color=#009900>// Uses template type induction to </font>
<font color=#009900>// discover the size of an array</font>
#ifndef ARRAYSIZE_H
#define ARRAYSIZE_H
<font color=#0000ff>template</font><<font color=#0000ff>typename</font> T, <font color=#0000ff>int</font> size>
<font color=#0000ff>int</font> asz(T (&)[size]) { <font color=#0000ff>return</font> size; }
#endif <font color=#009900>// ARRAYSIZE_H ///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This actually figures out the size of an
array as a compile-time constant value, without using any <B>sizeof( )</B>
operations! Thus you can have a much more succinct way to calculate the size of
an array at compile time:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:ArraySize.cpp</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#009900>//{-msc}</font>
<font color=#009900>//{-bor}</font>
<font color=#009900>// The return value of the template function</font>
<font color=#009900>// asz() is a compile-time constant</font>
#include <font color=#004488>"..</font><font color=#004488>/arraySize.h"</font>
<font color=#0000ff>int</font> main() {
<font color=#0000ff>int</font> a[12], b[20];
<font color=#0000ff>const</font> <font color=#0000ff>int</font> sz1 = asz(a);
<font color=#0000ff>const</font> <font color=#0000ff>int</font> sz2 = asz(b);
<font color=#0000ff>int</font> c[sz1], d[sz2];
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course, just making a variable of a
built-in type a <B>const</B> does not guarantee it’s actually a
compile-time constant, but if it’s used to define the size of an array (as
it is in the last line of <B>main( )</B>), then it <I>must</I> be a
compile-time constant.</FONT><A NAME="_Toc519041984"></A><BR></P></DIV>
<A NAME="Heading161"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Taking the address of a generated function template </H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are a number of situations where
you need to take the address of a function. For example, you may have a function
that takes an argument of a pointer to another function. Of course it’s
possible that this other function might be generated from a template function so
you need some way to take that kind of
address</FONT><A NAME="fnB16" HREF="#fn16">[16]</A><FONT FACE="Georgia">:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:TemplateFunctionAddress.cpp</font>
<font color=#009900>// Taking the address of a function generated</font>
<font color=#009900>// from a template.</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#0000ff>template</font> <<font color=#0000ff>typename</font> T> <font color=#0000ff>void</font> f(T*) {}
<font color=#0000ff>void</font> h(<font color=#0000ff>void</font> (*pf)(<font color=#0000ff>int</font>*)) {}
<font color=#0000ff>template</font> <<font color=#0000ff>class</font> T>
<font color=#0000ff>void</font> g(<font color=#0000ff>void</font> (*pf)(T*)) {}
<font color=#0000ff>int</font> main() {
<font color=#009900>// Full type exposition:</font>
h(&f<<font color=#0000ff>int</font>>);
<font color=#009900>// Type induction:</font>
h(&f);
<font color=#009900>// Full type exposition:</font>
g<<font color=#0000ff>int</font>>(&f<<font color=#0000ff>int</font>>);
<font color=#009900>// Type inductions:</font>
g(&f<<font color=#0000ff>int</font>>);
g<<font color=#0000ff>int</font>>(&f);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This example demonstrates a number of
different issues. First, even though you’re using templates, the
signatures must match – the function <B>h( )</B> takes a pointer to a
function that takes an <B>int*</B> and returns <B>void</B>, and that’s
what the template <B>f</B> produces. Second, the function that wants the
function pointer as an argument can itself be a template, as in the case of the
template <B>g</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>main( )</B> you can see that
type induction works here, too. The first call to <B>h( )</B> explicitly
gives the template argument for <B>f</B>, but since <B>h( )</B> says that
it will only take the address of a function that takes an <B>int*</B>, that part
can be induced by the compiler. With <B>g( )</B> the situation is even more
interesting because there are two templates involved. The compiler cannot induce
the type with nothing to go on, but if either <B>f</B> or <B>g</B> is given
<B>int</B>, then the rest can be
induced.</FONT><A NAME="_Toc312374091"></A><A NAME="_Toc519041985"></A><BR></P></DIV>
<A NAME="Heading162"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Local classes in templates<A NAME="_Toc519041986"></A></H2></FONT>
<A NAME="Heading163"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Applying a function to an STL
sequence<BR><A NAME="Index472"></A><A NAME="Index473"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Suppose you want to take an STL sequence
container (which you’ll learn more about in subsequent chapters; for now
we can just use the familiar <B>vector</B>) and apply a function to all the
objects it contains. Because a <B>vector</B> can contain any type of object, you
need a function that works with any type of <B>vector</B> and any type of object
it contains:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:applySequence.h</font>
<font color=#009900>// Apply a function to an STL sequence container</font>
<font color=#009900>// 0 arguments, any type of return value:</font>
<font color=#0000ff>template</font><<font color=#0000ff>class</font> Seq, <font color=#0000ff>class</font> T, <font color=#0000ff>class</font> R>
<font color=#0000ff>void</font> apply(Seq& sq, R (T::*f)()) {
<font color=#0000ff>typename</font> Seq::iterator it = sq.begin();
<font color=#0000ff>while</font>(it != sq.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> Seq, <font color=#0000ff>class</font> T, <font color=#0000ff>class</font> R, <font color=#0000ff>class</font> A>
<font color=#0000ff>void</font> apply(Seq& sq, R(T::*f)(A), A a) {
<font color=#0000ff>typename</font> Seq::iterator it = sq.begin();
<font color=#0000ff>while</font>(it != sq.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> Seq, <font color=#0000ff>class</font> T, <font color=#0000ff>class</font> R,
<font color=#0000ff>class</font> A1, <font color=#0000ff>class</font> A2>
<font color=#0000ff>void</font> apply(Seq& sq, R(T::*f)(A1, A2),
A1 a1, A2 a2) {
<font color=#0000ff>typename</font> Seq::iterator it = sq.begin();
<font color=#0000ff>while</font>(it != sq.end()) {
((*it)->*f)(a1, a2);
it++;
}
}
<font color=#009900>// Etc., to handle maximum likely arguments ///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>apply( ) </B>function
template takes a reference to the container class and a pointer-to-member for a
member function of the objects contained in the class. It uses an iterator to
move through the <B>Stack</B> and apply the function to every object. If
you’ve (understandably) forgotten the
pointer-to-member<A NAME="Index474"></A> syntax, you can refresh your memory at
the end of Chapter XX.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice that there are no STL header files
(or any header files, for that matter) included in <B>applySequence.h</B>, so it
is actually not limited to use with an STL sequence. However, it does make
assumptions (primarily, the name and behavior of the <B>iterator</B>) that apply
to STL sequences.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see there is more than one
version of <B>apply( )</B>, so it’s possible to overload function
templates. Although they all take any type of return value (which is ignored,
but the type information is required to match the pointer-to-member), each
version takes a different number of arguments, and because it’s a
template, those arguments can be of any type. The only limitation here is that
there’s no “super template” to create templates for you; thus
you must decide how many arguments will ever be required.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To test the various overloaded versions
of <B>apply( )</B>, the class
<B>Gromit</B></FONT><A NAME="fnB17" HREF="#fn17">[17]</A><A NAME="Index475"></A><FONT FACE="Georgia">
is created containing functions with different numbers of
arguments:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:Gromit.h</font>
<font color=#009900>// The techno-dog. Has member functions </font>
<font color=#009900>// with various numbers of arguments.</font>
#include <iostream>
<font color=#0000ff>class</font> Gromit {
<font color=#0000ff>int</font> arf;
<font color=#0000ff>public</font>:
Gromit(<font color=#0000ff>int</font> arf = 1) : arf(arf + 1) {}
<font color=#0000ff>void</font> speak(<font color=#0000ff>int</font>) {
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < arf; i++)
std::cout << <font color=#004488>"arf! "</font>;
std::cout << std::endl;
}
<font color=#0000ff>char</font> eat(<font color=#0000ff>float</font>) {
std::cout << <font color=#004488>"chomp!"</font> << std::endl;
<font color=#0000ff>return</font> 'z';
}
<font color=#0000ff>int</font> sleep(<font color=#0000ff>char</font>, <font color=#0000ff>double</font>) {
std::cout << <font color=#004488>"zzz..."</font> << std::endl;
<font color=#0000ff>return</font> 0;
}
<font color=#0000ff>void</font> sit(<font color=#0000ff>void</font>) {}
}; <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now the <B>apply( )</B> template
functions can be combined with a <B>vector<Gromit*></B> to make a
container that will call member functions of the contained objects, like
this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:applyGromit.cpp</font>
<font color=#009900>// Test applySequence.h</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
#include <font color=#004488>"Gromit.h"</font>
#include <font color=#004488>"applySequence.h"</font>
#include <vector>
#include <iostream>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>int</font> main() {
vector<Gromit*> 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));
apply(dogs, &Gromit::speak, 1);
apply(dogs, &Gromit::eat, 2.0f);
apply(dogs, &Gromit::sleep, 'z', 3.0);
apply(dogs, &Gromit::sit);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although the definition of
<B>apply( )</B> is somewhat complex and not something you’d ever
expect a novice to understand, its use is remarkably clean and simple, and a
novice could easily use it knowing only<I> what</I> it is intended to
accomplish, not <I>how</I>. This is the type of division you should strive for
in all of your program components: The tough details are all isolated on the
designer’s side of the wall, and users are concerned only with
accomplishing their goals, and don’t see, know about, or depend on details
of the underlying
implementation</FONT><A NAME="_Toc312374092"></A><A NAME="_Toc519041987"></A><BR></P></DIV>
<A NAME="Heading164"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Expression templates<A NAME="_Toc519041988"></A></H2></FONT>
<A NAME="Heading165"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Template-templates</H2></FONT>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C06:TemplateTemplate.cpp</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
<font color=#009900>//{-msc}</font>
#include <vector>
#include <iostream>
#include <string>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#009900>// As long as things are simple, </font>
<font color=#009900>// this approach works fine:</font>
<font color=#0000ff>template</font><<font color=#0000ff>typename</font> C>
<font color=#0000ff>void</font> print1(C& c) {
<font color=#0000ff>typename</font> C::iterator it;
<font color=#0000ff>for</font>(it = c.begin(); it != c.end(); it++)
cout << *it << <font color=#004488>" "</font>;
cout << endl;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -