为此,STL把function objects和adaptable function objects区分开来。adaptable
function object则会指明引数与返回型别为何,它会内含嵌套的typedefs,所以程序中可以
指定并使用那些型别。
STL提供两个基类unary_function和binary_function,这使得定义Adaptable Unary Function
和Adaptable Binary Function的工作简化许多。
Function Object Adapters函数对象配接器:
Adapter配接器是一种将某接口转换成另一接口的组件。上面讲到的reverse_iterator就是一
个iterator adapter,
就像所有containers一样,sequence以严格线性顺序的range来呈现其元素。此外,你不但可
以取用任何元素,也可以在range的任意一个地点新增或删除元素。sequence都必须具有
member function insert与erase.如果S是Sequence而p是指向S内某个元素的iterator,
则S.erase(p)会移除并摧毁该元素。类似的,S.insert(p,x)会产生一个新对象,些对象是
X的一份副本,然后被安插于p所指元素之前。
function objects和adaptable function objects之间有一个重要而有点微妙的差异。
一般而言,function objects的引数型别受到限制,型别限制不一定是很单纯的,例如operator()
可能被重载,也可能是个member template,亦或两都都是。
adaptable function object可以指定引数及返回值型别,而且可以包含嵌套型别nested type
声明,好让这些型别可以被命名以及被程序使用。例如,如果F2是adaptable binary generator
的一个model,那么它必须定义嵌套型别F2::first_argument_type,F2::second_argument_type
和F2::result_type。
Range Constructor(区间构造函数):
X(i, j);
X a(i, j);
i和j是input iterators,其value type可转换成T。语义:产生一个associative container,内
含[i, j)的所有元素,每个元素具有独一无二的key。必然结果:size()<=distance(i, j)。
这是因为[i, j)之中可能有一些元素的key相同,果真如此便只会安插这些元素中第一个元素。
Insert element(安插元素):
a.insert(t);
返回pair<X::iterator, bool>
语义:当且仅当if and only if a没有任何一个元素与t的键值冲突,则将t安插于a。返回值是
pair p,其第二个元素的型别为bool。如果a的某个元素与t的键值冲突,则p.first指向该元素
p.second为false,如果a并未含有这样的元素,则p.first指向新插入的元素并且p.second为
true。也就是说,当t实际被安插于a之中,p.second便为true.
Insert range(安插某个区间):
a.insert(i, j);
i, j都是input iterators,其value type可转换为T。
返回void,相当于对rang[i,j)中的每个元素t做a.insert(t)动作。也就是说,只要a的元素和
range内的元素不产生键值冲突的情况,整个range便都可以安插进入a.
Range construct(以某区间为初值的构造函数):
X(i, j);
X a(i, j);
i和j皆为input iterators,其value type可转换为T。等价于X a; a.insert(i, j);insert动
作带来的影响,也就是说它是否会将[i, j)的所有元素安插进去,抑或只是单一元素,取决于
x是multiple associative container或是unique associative container的model。
Range constructor with bucket count(接受bucket数量之区间构造函数):
X(i, j, n);
X a(i, j, n);
i和j皆为input iterators,其value type可转换为T。等价于X a(n); a.insert(i, j);
Range constructor with hash function(接受hash function之区间构造函数):
X(i, j, n, h);
X a(i, j, n, h);
i和j皆为input iterators,其value type可转换为T。等价于X a(n, h); a.insert(i, j);
Range constructor with key equality(接受key equality function之区间构造函数):
X(i, j, n, h, k);
X a(i, j, n, h, k);
i和j皆为input iterator,其value type可转换为T。等价于X a(n, h, k); a.insert(i, j);
以下找寻第一个大于其后继都的元素---也就是寻找range之内不以递增次序排列的第一个元素位
置。本例运用第二版本:
int main()
{
int A[] = {1, 2, 3, 4, 5, 6, 7, 8};
const int N = sizeof(A) / sizeof(int);
const int* p = adjacent_find(A, A + N, greater<int>());
if (p == A + N)
cout<<"The range is sorted in ascending order."<<endl;
else
cout<<"Element "<<p - A<<"is out of order: "<<*p
<<">"<<*(p+1)<<"."<<endl;
}
int main()
{
const char *s1 = "This is a Test";
const char *s2 = "This is a test";
const int N = strlen(s1);
pair<const char *, const char*>result =
mismatch(s1, s1+N, s2, eq_nocase);
if (result.first == s1 + N)
cout<<"The two strings do not differ"<<endl;
else
{
cout<<"Ths strings differ starting at character"
<<result.first - s1<<endl;
cout<<"Trailing part of s1: "<<result.first<<endl;
cout<<"Trailing part of s2: "<<result.second<<endl;
}
}
样例:
int main()
{
const int N = 8;
int A[] = {1, 2, 4, 5, 6, 7, 8};
random_shuffle(A, A + N);
copy(A, A+N, ostream_iterator<int>(cout, " "));
cout<<endl;
//结果可能是7 1 6 3 2 5 4 8
//或40319种其他可能性之一。
}
12.9.2 random_sample
(1)
template <class InputIterator, class RandomAccessIterator>
RandomAccessIterator random_sample(InputIterator first, InputIterator last,
RandomAccessIterator ofirst, RandomAccessIterator olast);
(2)
template <class InputIterator, class RandomAccessIterator,
class RandomNumberGenerator>
RandomAccessIterator random_sample(InputIterator first, InputIterator last,
RandomAccessIterator ofirst, RandomAccessIterator olast,
RandomNumberGenerator &rand);
算法random_sample会随机地将[first, last)内的一个取样结果复制到[ofirst, olast)中。它会
复制n个元素,此处n为min(last-first, olast-ofirst)。返回值为ofirst+n。
同样,random_sample有两个版本,其差别在于乱数的取得。第一个版本使用内部乱数产生器,
第二个版本使用random Number Generator,那是一种特殊的function object,被当成引数传递
进来。
如果你的程序需要保持input range中的元素的相对顺序,你应该改用random_sample_n。
12.9.3 random_sample_n
(1)
template <class ForwardIterator, class OutputIterator, class Distance>
OutputIterator random_sample_n(ForwardIterator first, ForwardIterator last,
OutputIterator out, Distance n);
(2)
template <class ForwardIterator, class OutputIterator, class Distance,
class RandomNumberGenerator。
OutputIterator random_sample_n(ForwardIterator first, ForwardIterator last,
OutputIterator out, Distance n,
RandomNumberGenerator &rand);
算法random_sample_n能够随机地将[first, last)中的某些元素复制到[out, out+n)中。它
会复制m个元素,此处m为min(last-first, n).返回值为ofirst+m。
random_sample_n与random_sample之间一个重要差别在于,random_sample_n会保持元素在input
range中的相对顺序,而random_sample不会。random_sample_n还必须保证有足够大的空间容纳
所有被复制的元素。
样例:
int main()
{
const int N = 10;
int A[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
random_sample_n(A, A+N, ostream_iterator<int>(cout, " "), 4);
cout<<endl;
//打印结果可能是3 5 6 10
//或209种可能性中的任何一种
}
12.10 一般化之数值算法(Generalized Numeric Algorithms)
12.10.1 accumulate
(1)
template <class InputIterator, class T>
T accumulate<InputIterator first, InputIterator last, T init);
(2)
template <class InputIterator, class T, class BinaryFunction>
T accumulate<InputIterator first, InputIterator last, T init,
BinaryFunction binary_op);
算法accumulate为总和运算的一般型。它可以计算init和[first, last)内的所有元素的总和
或其他双参运算。
注意,accumulate的接口有点笨拙。你一定得提供一个初始值init(这么做的原因之一是当
[first, last)为空时仍获一个明确定义的值。)如果你希望计算[first, last)中所有数值
的总和,你应该将init设为0。
式中的双参操作符不必具备交换性commutative和结合性associative。所有的accumulate运算
行为的顺序都有明确设定:首先将init初始化,然后针对[first, last)之每一个iterator i,
由起头到结尾,执行result = result + *i版本一或result = binary_op(result, *i)版本二。
样例:
计算range内所有元素的总和以及乘积。本例同时示范accumulate两个版本的用法。由于加法与
乘法具有不同的证同元素(identity element),所以计算总和时,提供0为初始值,计算乘积时
则以1为初始值。
int main()
{
int A[] = {1, 2, 3, 4, 5};
const int N = sizeof(A)/sizeof(int);
cout<<"The sum of all elements in A is "
<<accumulate(A, A+N, 0)<<endl;
cout<<"The product of all elements in A is "
<<accumulate(A, A+N, 1, multiplies<int>())<<endl;
}
12.10.2 inner_product
(1)
template <class InputIterator1, class InputIterator2, class T>
T inner_product(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2,
T init);
(2)
template <class InputIterator1, class InputIterator2, class T,
class BinaryFunction1, class BinaryFunction2>
T inner_product(InputIterator1 frist1, InputIterator1 last1,
InputIterator2 frist2, T init,
BinaryFunction1 binary_op1,
BinaryFunction2 binary_op2);
算法inner_product能够计算[first1, last1)和[first2, frist2+(last1-first1))的一般化
内积generalized inner product。如果你想计算两个vectors的一般内积,应该将init设为0.
样例:
将1!至20!的值填入一个表格中。
int main()
{
const int N = 20;
vector<double> v(N);
for (int i = 0; i < N; ++i)
v[i] = i + 1;
partial_sum(v.begin(), v.end(), v.begin(), multiplies<double>());
copy(v.begin(), v.end(), ostream_iterator<double>(cout, "\n");
}
15.2.4 divides
divides<T>
class divides<T>是一种Adaptable Binary Function。如果f是class divides<T>的一个object
且x和y都属于型别T,则f(x, y)返回x/y。
样例:
将vector内的每个元素都除以某个常量。
int main()
{
const int N = 1000;
vector<double> v(N);
generate<v.begin(), v.end(), rand);
transform(v.begin(), v.end(), v.begin(),
bind2nd(divides<double>(), double(RAND_MAX)));
for (int i = 0; i < N; ++i)
assert(0 <= v[i] && v[i] <= 1.);
}
15.3.3 less
class less<T>
class less<T>是一种Adaptable Binary Predicate,这表示它可以用来验证某条件之真伪。如
果f是class less<T>的一个object而且x和y都是型别T之值,则只有当x<y时f(x, y)才返回true.
STL的许多classes及algorithm都需要用到比较函数,例如sort,set,map。less是其典型的缺
省值。
样例:
重新排列一个数组,使所有负数排在非负数之前。
int main()
{
const int N = 10;
int A[N] = {1, -3, -7, 2, 5, -9, -2, 1, 6, -8};
15.7 Member Function Adapters
所谓member function adapters是一群小型的classes,让你能够将member functions当作function
objects来调用。每一个adapter需要一个型别为X*或X&的引数,可通过该引数调用X的一个member
function----如果那是一个virtual member function,那么便是一个多态函数调用polymorphic
function object,因此,member function adapters是面向对象编程与泛型编程之间的桥梁。
Member function adapters数量之多令人却步,但它们都遵循系统化的命名格式。三个不同的
特点形成了共2^3 = 8种adapters:
1、这个adapters接受型别为X*或X&的引数吗?如果是后者,其名称有"_ref"后缀词。
2、这个adapters封装的是无引数的或单一引数的member function?如果是后者,其名称有‘1‘
后缀词。
3、这个adapters封装的是non_const或const member function?(C++的型别声明语法无法以
同一个class处理两种情况)如果是后者,其名称会冠上"const_"前缀词。
15.7.1 mem_fun_t
mem_fun_t<R, X>
class mem_fun_t是一种member function adapter。如果X是个class,具有member function
R X::f()亦即无任何引数,返回型别为R,那么mem_fun_t<R, X>便成为一个member object
adapter,使得以一般函数(而非member function)的方式来调用f()。
如同其他众多adapters一样,直接使用mem_fun_t constructor是很不方便的。使用辅助函数
mem_fun来代替往往是比较好的做法。
如果F是一个mem_fun_t,并且X是一个指针,型别为X*,那么表达式F(X)等价于表达式X->f().
样例:
struct B{
virtual void print() = 0;
};
struct D1: public B{
void print(){cout<<"I‘m a D1"<<endl;}
};
struct D2: public B{
void print(){cout<<"I‘m a D2"<<endl;}
};
可移植性与标准化
C++ standard与ARM之间的差异,就像ANSI C standard与The C Programming Language之间的差
异一样。几乎所有C++编译器所实现出来的,都介于ARM C++与standard C++之间。
STL的情况也很类似。仍然没有一个STL实现品完全符合Stepanov--Lee的原始定义,或是符合C++
standard的定义。第二个问题是,即使到了今天,也没有任何一个C++编译器支持STL所用到的每
一个语言特性。
A.1.2 带缺省值的Template参数(Default Template Parameters)
就你C++函数的参数可具有缺省值,class templates也一样。假设你声明一个class template
如下:
template <class A, class B = A>
class X{
...
};
那么当你具现化instantiated X时,可以不指定第二个template参数。这么一来便会采用缺省值
A。是的,型别X<int>与型别X<int, int>完全相同。
所有的STL container classes都使用带缺省值的template参数。例如,vector就具有两个
template参数,第一个表现出vector的元素型别,第二个表现allocator,用来参数化vector
的内存分配策略。第二个template参数便带有缺省值,因为大部分的vector使用时机都没有理
由使用非标准的内存分配策略。
目前,并非所有的C++编译器都完全支持带缺省值的template参数。
A.1.3 Member Templates
templates最初加入C++语言时,只有两种东西可以是template:全局的classes和全局的functions
也就是说,template声明式不能在class scope内发生。这意味着member functions不能是
function template。
如今C++ standard已允许non-virtual member functions可以成为function templates。
Member function templates的调用方式和一般的function templates一样。编译器可以由函数
调用所给定的引数推导出template参数,并自动产生该member function的适当实体instance。
A.1.4 偏特化Partial Specialization
class templates的特化如今进展到了所谓的偏特化partial specialization,如下,你可以针对
整个型别类属entire category of types,而非只针对单一型别,给予template一份不同的定义:
template <clas T> class X
{
//版本1:最一般化(泛化)
};
template <class T> class X<T *>
{
//版本2:针对普通指针、
};
template <> class X(void *)
{
//版本3: 针对某种特定指针
};
全特化与偏特化并不使用相同的语法。这是C++标准化过程中的一项改变。
如果你所使用的编译器遵循C++ standard,你必须写
template <> class X<void>
如果你使用的编译器比较旧,你得这么写:
class X<void *>
这个式子并未使用关键字template。除了使用预处理器pre-processor所提供的宏,否则再没有其
他方法可以写出符合新旧规格的全特化语法。
C++ standard已经改变这一点。stack如今需要两个template参数。第一个是存储于该stack内
的object型别,第二个才是该stack的底部数据组织。当然后者是一个带缺省值的template参数。
对stack而言,该缺省值为deque。因此,由ints组成的stack可以声明为stack<int>。
A.2.3 次要的程序库变动
标准化过程中,STL的接口于某些次要部分亦有些微的改变及扩充。
新的算法
C++ standard内含三个并未定义于原始HP STL中的算法:
1、find_first_of,这很类似C函数库函数strpbrk
2、find_end,它很类似STL算法search
3、search_n