泛型编程一 Traits,容器,仿函数以及仿函数适配器 Shepard-Wang

一 特性 Traits

假设给定一个数组,计算数组中所有元素的和:

template <typename T>
inline T
Sigma(const T* start, const T* end)
{
    T total = T();
    while(start != end)
    {
        total += *start;
        start++;
    }
    
    return total;
}

当我们使用 char 类型调用模板函数时:

char arr2[3] = {'a', 'b', 'c'};

cout << (int)Sigma(arr2, arr2 + 3) << endl;

发现输出的是 38,并不是 a,b,c ASCII 码值的和,这是因为 char 类型最大存储 127,而 a,b,c 的 ASCII 码值超过了 char 类型的最大存储量,导致超出的部分被截断,只保留后 8 字节的信息。

同理 int 和其他类型也可能发生这种越界情况!这种情况应该怎么办?

解决的办法就是为每个 Sigma 函数参数类型分别创建一种关联,关联的类型就是用来存储 Sigma 结果的类型。

这种关联可以看作是 Sigma 函数的一种特性,因此 SIgma 函数的返回值的类型就叫做 T 的 trait

Traits 可以实现为模板类,而关联则是针对每个具体类型 T 的特化。

class SigmaTraits {};

template <>
class SigmaTraits<char>
{
public:
    typedef int ReturnType;
};

template <>
class SigmaTraits<short>
{
public:
    typedef int ReturnType;
};

template <>
class SigmaTraits<int>
{
public:
    typedef long ReturnType;
};

template <>
class SigmaTraits<float>
{
public:
    typedef double ReturnType;
};

修改函数 Sigma 的返回值类型:

template <typename T>
inline typename SigmaTraits<T>::ReturnType
Sigma(const T* start, const T* end)
{
    typename SigmaTraits<T>::ReturnType total = T();
    while(start != end)
    {
        total += *start;
        start++;
    }

    return total;
}

调用端:

int main()
{
    int arr[3] = {1, 2, 3};
    char arr2[3] = {'a', 'b', 'c'};

    typename SigmaTraits<char>::ReturnType sum = Sigma(arr2, arr2 + 3);

    cout << sum << endl;

    return 0;
}

二 迭代器 iterator

迭代器是泛化的指针

迭代器本身是一个对象,指向另一个(可迭代的)对象

迭代器是 STL 算法和容器的接口,它解耦算法和容器

容器只要提供一种方式,可以让迭代器访问容器元素

template<class _Init, class _Ty>
inline _Init find(_Init first, _Init last, const _Ty& val)
{
    for(; first != last; ++first)
        if(*first == val)
            return first;
    return first;
}

调用:

int main()
{
    vector<int> v{2, 3, 4};
    list<int> l{2, 3, 4};
    deque<int> d{2, 3, 4};

    int target = 3;

    vector<int>::iterator vi = find(v.begin(), v.end(), target);
    list<int>::iterator   li = find(l.begin(), l.end(), target);
    deque<int>::iterator  di = find(d.begin(), d.end(), target);

    cout << "vector : " << *vi << endl;
    cout << "list : "   << *li << endl;
    cout << "deque : "  << *di << endl;

    return 0;
}

三 容器 Container

1、vector

数据结构和操作与 array 类似,内存中的表现是一段 地址连续 的空间。与 array 不同的是,vector 支持动态空间大小调整。

创建

访问

  • operator[] 提供类似数组的访问方式,不做边界检查,可能越界访问,但是效率较高
  • at() 进行边界检查,越界抛出异常,效率不如 operator[]

删除

  • clear() 清除整个 vector注意:vector 容量不保证改变,不保证 reallocation 。此函数的替代写法:

    vector<int> x;
    // ...
    vector<int>().swap(x);
    
  • pop_back() 尾删。vectorsize 减 1,调用被删除元素析构。由于不做范围检查,容器为空时删除可能造成程序崩溃。

  • erase() 删除 vector 中某一位置的元素。

    • 用法 1:删除 iterator指定位置的元素

      std::vector<int>::const_iterator it = v.begin();
      v.erase(it + 1);
      
    • 用法 2:通过某一条件函数(返回 true/false)找到 vector 中要删除的元素。以 remove_if() 为例

iterator erase(const_iterator1, const_iterator2)

删除 [const_iterator1, const_iterator2) 区间内的所有元素

#include <algorithm>

struct ContainString : public unary_function<string, bool>
{
    ContainString(const string& str) : str_match(str) {}

    bool operator()(const string& str2match) const
    {
        return str2match.find(str_match) != -1;
    }

    string str_match;
};

int main()
{
    vector<string> v;

    v.push_back("I love C++");
    v.push_back("I love you");
    v.push_back("C++ doesn't love me");
	
    // 删除所有 ContainString(str) 返回值为 true 的元素
    // 传入参数时的 ContainString("C++") 调用的是 ContainString 的构造函数
    // remove_if 内的函数调用是 operator()
    v.erase( remove_if(v.begin(), v.end(), ContainString("C++")), 
            v.end()
           );

    for(auto s : v)
        cout << s << endl;
}

forward_iterator remove_if(forward_iterator first, forward_iterator last, pred)

remove_if 并没有删除任何元素,只是改变了元素的位置。将满足删除条件的元素放到 last 之前。这样说可能不够具体,详细的可以看代码:

template<typename _ForwardIterator, typename _Predicate>
inline _ForwardIterator
remove_if(_ForwardIterator __first, _ForwardIterator __last,
          _Predicate __pred)
{
    return std::__remove_if(__first, __last,
                            __gnu_cxx::__ops::__pred_iter(__pred));
}

template<typename _InputIterator, typename _Predicate>
inline _InputIterator
__find_if(_InputIterator __first, _InputIterator __last,
          _Predicate __pred, input_iterator_tag)
{
    while (__first != __last && !__pred(__first))
        ++__first;
    return __first;
}

template<typename _ForwardIterator, typename _Predicate>
_ForwardIterator
__remove_if(_ForwardIterator __first, _ForwardIterator __last,
            _Predicate __pred)
{
    __first = std::__find_if(__first, __last, __pred);
    if (__first == __last)
        return __first;
    _ForwardIterator __result = __first;
    ++__first;
    for (; __first != __last; ++__first)
        if (!__pred(__first))
        {
            *__result = _GLIBCXX_MOVE(*__first);
            ++__result;
        }
    return __result;
}

移动元素的操作在 __remove_if 中:

for (; __first != __last; ++__first)
    if (!__pred(__first))
    {
        // __result 位置为待删除元素,直接用不删除的元素覆盖要删除的元素
        // #define _GLIBCXX_MOVE(__val) std::move(__val) 
        // 赋值时使用右移版本的 operator= 
        *__result = _GLIBCXX_MOVE(*__first);
        ++__result;
    }

2、list

创建

删除

  • clear():内部调用 erase(begin(), end())
  • pop_back() 尾删
  • pop_front() 头删
  • remove() 删除 list 指定值元素
  • erase() 同样是两个版本,删除迭代器指定位置的元素,删除迭代器指定区间的元素

与 vector 类似:

list<string> l{ string("C++ love me"), string("who love me"), string("nobody love me") };
//l.remove_if(ContainString("C++"));
l.erase(remove_if(l.begin(), l.end(), ContainString("C++")), l.end());

连接

splice

l1.splice(iterator, l2) 将 l2 所有元素加入 l1 的 iterator 位置并删除 l2 所有元素

list<string> l{ string("C++ love me"), string("who love me"), string("nobody love me") };

list<string> l2 { string("everyone loves me!"), string("I'm beautiful") };

l.splice(l.begin(), l2);

for(auto& e : l)
{
    cout << e << endl;
}

cout << "==================" <<endl;

for(auto& e : l2)
{
    cout << e << endl;
}

everyone loves me!
I'm beautiful
C++ love me
who love me
nobody love me
==================

l3.splice(l3.begin(), l, it1)l 中迭代器 it1 所指向的元素加入 l3 的迭代器 l3.begin() 所指向的元素前

list<string> l{ string("C++ love me"), string("who love me"), string("nobody love me") };

list<string> l2 { string("everyone loves me!"), string("I'm beautiful") };

list<string> l3;

auto it1 = l.begin();
advance(it1, 1);

l3.splice(l3.begin(), l, it1);

for(auto& e : l)
{
    cout << e << endl;
}

cout << "==================" <<endl;

for(auto& e : l3)
{
    cout << e << endl;
}

C++ love me
nobody love me
==================
who love me

l.splice(l.begin(), l, it1, l.end())l[it1, l.end()) 范围内的元素加入 ll.begin() 指示的位置前

list<string> l{ string("C++ love me"), string("who love me"), string("nobody love me") };

auto it1 = l.begin();
advance(it1, 1);

l.splice(l.begin(), l, it1, l.end());

for(auto& e : l)
{
    cout << e << endl;
}

who love me
nobody love me
C++ love me

3、map\multimap

template < class Key,                                     // map::key_type
           class T,                                       // map::mapped_type
           class Compare = less<Key>,                     // map::key_compare
           class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
           > class map;
  • key 不可修改,不能重复
  • key 具有 operator< 行为
  • 可自定义排序函数,默认采用 less
struct Employee
{
    Employee() {}
    Employee(const string& str) : m_name(str) {}

    string m_name;
};

template<typename T>
struct CmpById : binary_function<T, T, bool>
{
    bool operator()(const T& left, const T& right) const
    {
        return left > right;
    }
};

int main()
{
    map<int, Employee, CmpById<int>> m;

    m.insert( make_pair(3, Employee("none")) );
    m.insert( make_pair(1, Employee("wxc")) );
    m.insert( make_pair(2, Employee("hc")) );

    for(auto& e : m)
    {
        cout << e.second.m_name << endl;
    }


    return 0;
}

none
hc
wxc

multimap 允许 key 重复

int main()
{
    multimap<int, Employee, CmpById<int>> m;

    m.insert( make_pair(3, Employee("none")) );
    m.insert( make_pair(1, Employee("wxc")) );
    m.insert( make_pair(2, Employee("hc")) );
    m.insert( make_pair(2, Employee("hc")) );

    cout << m.count(2) << endl;

    return 0;
}

2

4、set\multiset

template < class T,                        // set::key_type/value_type
           class Compare = less<T>,        // set::key_compare/value_compare
           class Alloc = allocator<T>      // set::allocator_type
           > class set;
  • key 不可修改,不能重复。
  • key 和 value 相同
  • 对象具有 operator< 行为
  • 可自定义排序函数,默认采用 less

set_union(s.begin(), s.end(), s2.begin(), s2.end(), ii, CmpById< Employee >());

并集

struct Employee
{
    Employee() {}
    Employee(const string& str, const int age) : m_name(str), m_age(age) {}

    bool operator<(const struct Employee& right) const
    {
        return m_age < right.m_age;
    }

    string m_name;
    int m_age;
};

template<typename T>
struct CmpById : binary_function<T, T, bool>
{
    bool operator()(const T& left, const T& right) const
    {
        return !(left < right);
    }
};

set<Employee, CmpById<Employee>> s;
s.insert(Employee("wxc", 22));
s.insert(Employee("hc", 28));
s.insert(Employee("wxxc", 0));

set<Employee, CmpById<Employee>> s2;
s2.insert(Employee("jack", 21));
s2.insert(Employee("john", 24));
s2.insert(Employee("shepard", 30));

set<Employee, CmpById<Employee>> dest;

insert_iterator<set<Employee, CmpById<Employee>>> ii(dest, dest.begin());
set_union(s.begin(), s.end(), s2.begin(), s2.end(), ii, CmpById<Employee>());

for(auto e : dest)
{
    cout << e.m_age << " " << e.m_name << endl;
}

30 shepard
28 hc
24 john
22 wxc
21 jack
0 wxxc

set_intersection(s.begin(), s.end(), s2.begin(), s2.end(), ii, CmpById< Employee >());

交集

template<typename T>
struct CmpById : binary_function<T, T, bool>
{
    bool operator()(const T& left, const T& right) const
    {
        return (left < right);
    }
};


set<Employee, CmpById<Employee>> s;
s.insert(Employee("wxc", 22));
s.insert(Employee("hc", 28));
s.insert(Employee("wxxc", 0));

for(auto e : s)
{
    cout << e.m_age << " " << e.m_name << endl;
}

set<Employee, CmpById<Employee>> s2;
s2.insert(Employee("wxc", 21));
s2.insert(Employee("wxc", 22));
s2.insert(Employee("hc", 28));

for(auto e : s2)
{
    cout << e.m_age << " " << e.m_name << endl;
}

set<Employee, CmpById<Employee>> dest;

insert_iterator<set<Employee, CmpById<Employee>>> ii(dest, dest.begin());
set_intersection(s.begin(), s.end(), s2.begin(), s2.end(), ii, CmpById<Employee>());

注意仿函数对象对 operator() 的重载不能写成这样:

template<typename T>
struct CmpById : binary_function<T, T, bool>
{
    bool operator()(const T& left, const T& right) const
    {
        return !(left < right);
    }
};

不能对结果取反,否则交集会算错。

因为 union_intersection 的判断原理是这样的:对于 set1 的元素 s1set2 的元素 s2,如果 ( comp(s1, s2) == false ) && ( comp(s2, s1) == false ) 则认为 s1 == s2 。若写成上面错误的形式,本来 comp(s1, s2) == false 取反成 true 了。

源代码如下:

	template<typename _InputIterator1, typename _InputIterator2,
			typename _OutputIterator,
			typename _Compare>
	_OutputIterator
	__set_intersection(_InputIterator1 __first1, _InputIterator1 __last1,
					   _InputIterator2 __first2, _InputIterator2 __last2,
					   _OutputIterator __result, _Compare __comp)
	{
		while (__first1 != __last1 && __first2 != __last2)
			if (__comp(__first1, __first2))
				++__first1;
			else if (__comp(__first2, __first1))
				++__first2;
			else
			{
				*__result = *__first1;
				++__first1;
				++__first2;
				++__result;
			}
		return __result;
	}

set_difference(s.begin(), s.end(), s2.begin(), s2.end(), ii, CmpById< Employee >());

差集

set<Employee, CmpById<Employee>> s;
s.insert(Employee("wxc", 22));
s.insert(Employee("hc", 28));
s.insert(Employee("wxxc", 0));

set<Employee, CmpById<Employee>> s2;
s2.insert(Employee("wxc", 21));
s2.insert(Employee("wxc", 22));
s2.insert(Employee("hc", 28));

set<Employee, CmpById<Employee>> dest;

insert_iterator<set<Employee, CmpById<Employee>>> ii(dest, dest.begin());
// s - s2
set_difference(s.begin(), s.end(), s2.begin(), s2.end(), ii, CmpById<Employee>());

// 0 wxxc

如果在 set_difference 中改变参数的顺序,则调用结果不同:

// s2 - 2
set_difference(s2.begin(), s2.end(), s.begin(), s.end(), ii, CmpById<Employee>());

//21 wxc

set_difference() 的原理是:对于有序的(排序规则相同)集合 set1 和 set2, set1 的元素 s1set2 的元素 s2,如果 comp(s1, s2) == true 则认为 s1 不存在于 s2

template<typename _InputIterator1, typename _InputIterator2,
			typename _OutputIterator,
			typename _Compare>
	_OutputIterator
	__set_difference(_InputIterator1 __first1, _InputIterator1 __last1,
					 _InputIterator2 __first2, _InputIterator2 __last2,
					 _OutputIterator __result, _Compare __comp)
	{
		while (__first1 != __last1 && __first2 != __last2)
            // first1 不存在于 first2
			if (__comp(__first1, __first2))
			{
				*__result = *__first1;
				++__first1;
				++__result;
			}
            // first2 不存在于 first1
			else if (__comp(__first2, __first1))
				++__first2;
            // 共同元素
			else
			{
				++__first1;
				++__first2;
			}
		return std::copy(__first1, __last1, __result);
	}

对于 Employee 对象,真正参与比较的只有 m_age 字段:

struct Employee
{
    Employee() {}
    Employee(const string& str, const int age) : m_name(str), m_age(age) {}

    bool operator<(const struct Employee& right) const
    {
        return m_age < right.m_age;
    }

    string m_name;
    int m_age;
};

template<typename T>
struct CmpById : binary_function<T, T, bool>
{
    bool operator()(const T& left, const T& right) const
    {
        return left < right;
    }
};

所以只有 m_age 是真正的 key,其他的字段并不是 key。但是如果想要修改其他字段:

auto it = s.find(Employee("wxc", 22));
if(it != s.end())
    it->m_name = "123";

这样的修改是非法的!

若要修改非 key 字段,可通过如下手段:

auto it = s.find(Employee("wxc", 22));
if(it != s.end())
    const_cast<Employee&>(*it).m_name = "123";

for(auto e : s)
    cout << e.m_name << endl;

wxxc
123
hc

注意一定要将 *it 转换为引用类型,不然修改的是临时对象的值!

 if(it != s.end())
        ((Employee)(*it)).m_name = "123";

set 中的元素并没有修改!上面的代码等价于:

Employee tmp(*it);
tmp.m_name = "123";

四 仿函数 Functors

仿函数又称函数对象(function object),作用相当于一个函数指针

回顾之前对 remove_if 的调用:

remove_if(v.begin(), v.end(), ContainString("C++"));

ContainString 一般实现为一个 structclass ,所以 ContainString() 就是创建一个 ContainString 对象。remove_if 中传入了一个 ContainString临时对象并调用其构造函数,参数为字符串 "C++"

示例 1:

template<typename T>
struct Less : binary_function<T, T, bool>
{
    bool operator()(const T& left, const T& right) const
    {
        return left < right;
    }
};

int main()
{
    Less<int> less;
    cout << less(10, 3) << endl;
    // boolalpha 将布尔类型的值字符串化
    cout << std::boolalpha << less(3, 10) << endl;

    return 0;
}

0
true

实例 2 :使用 for_each()

struct PrintEmployee : unary_function<Employee&, void>
{
    ostream& os;
    explicit PrintEmployee(ostream& out) : os(out) {}
    void operator()(const Employee& employee) const
    {
        os << employee.m_name << " " << employee.m_age << std::endl;
    }
};

int main()
{
    vector<Employee> employees{Employee("wxc", 22), Employee("hc", 28)};
	// 对 [it1, it2) 区间的每个元素调用 PrintEmployee()
    for_each(employees.begin(), employees.end(), PrintEmployee(cout));

    return 0;
}

为什么要用仿函数而不是普通指针?

  • 函数指针不能满足 STL 的抽象要求
  • 函数指针无法和 STL 的其他组件交互
  • 仿函数可作为模板实参用于定义对象的某种默认行为

set 默认以 less 作为元素的排序方式,两个不同排序方式的 set 无法进行赋值或判等!

set<int, less<int>> s_less1;
set<int, less<int>> s_less2;
set<int, greater<int>> s_greater;

s_less1 == s_less2; // ok
s_less1 == s_greater; // error

如果要用 set 存储自定义的类,自定义的类应满足可比较大小要求(重载 operator<operator>等函数),如果使用 set 默认的 less 作为比较规则,则应该重载 operator< 函数

struct Employee
{
    Employee() {}
    Employee(const string& str, const int age) : m_name(str), m_age(age) {}

    bool operator<(const struct Employee& right) const
    {
        return m_age < right.m_age;
    }
    
    string m_name;
    int m_age;
};

template<typename _Tp>
struct less : public binary_function<_Tp, _Tp, bool>
{
    _GLIBCXX14_CONSTEXPR
    bool
    operator()(const _Tp& __x, const _Tp& __y) const
    { return __x < __y; }
};

五 仿函数适配器 Functor Adapters

仿函数适配器旨在将无法匹配的仿函数套接成可以匹配的类型

1、binder1st

给定一个数组 [0, 0, 0, 3, 2, 1] ,如何通过 std::not_equal_to 匹配得到第一个非零元素?一个可以想象的调用:

vector<int> v{0, 0, 0, 3, 2, 1};

find_if(v.begin(), v.end(), not_equal_to<int>(0));

但这样是行不通的,我们来看一下 not_equal_to 的实现:

   template<typename _Tp>
    struct not_equal_to : public binary_function<_Tp, _Tp, bool>
    {
        _GLIBCXX14_CONSTEXPR
        bool
        operator()(const _Tp& __x, const _Tp& __y) const
        { return __x != __y; }
    };

not_equal_to 没有单参构造,且是一个二元运算符!

int main()
{
    vector<int> v{0, 0, 0, 3, 2, 1};

    not_equal_to<int> net;
    typename not_equal_to<int>::first_argument_type tar = 0;
    std::binder1st<std::not_equal_to<int>> bind1(net, tar);
    
    auto it = find_if(v.begin(), v.end(), bind1);
    if(it != v.end())
        cout << *it << endl;

    return 0;
}

binder1st 实现

template<typename _Operation>
    class binder1st
    : public unary_function<typename _Operation::second_argument_type,
			    typename _Operation::result_type>
    {
    protected:
      _Operation op;
      typename _Operation::first_argument_type value;

    public:
      binder1st(const _Operation& __x,
		const typename _Operation::first_argument_type& __y)
      : op(__x), value(__y) { }

      typename _Operation::result_type
      operator()(const typename _Operation::second_argument_type& __x) const
      { return op(value, __x); }

      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // 109.  Missing binders for non-const sequence elements
      typename _Operation::result_type
      operator()(typename _Operation::second_argument_type& __x) const
      { return op(value, __x); }
    } _GLIBCXX_DEPRECATED;

_Operation 类型就是 not_equal_to<int> ,要寻找的目标 tar 绑定到了 value 上,作为 not_equal_to() 的第一参数,find_if 以此传入 vector 的每个元素作为 not_equal_to 的第二参数。

find_if调用 binder1st 时:binder1st(*iterator) 实际调用的是 op(value, *iterator) 也就是 not_equal_to(tar, x) x 为数组元素。

2、binder2nd

  template<typename _Operation>
    class binder2nd
    : public unary_function<typename _Operation::first_argument_type,
			    typename _Operation::result_type>
    {
    protected:
      _Operation op;
      typename _Operation::second_argument_type value;

    public:
      binder2nd(const _Operation& __x,
		const typename _Operation::second_argument_type& __y)
      : op(__x), value(__y) { }

      typename _Operation::result_type
      operator()(const typename _Operation::first_argument_type& __x) const
      { return op(__x, value); }

      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // 109.  Missing binders for non-const sequence elements
      typename _Operation::result_type
      operator()(typename _Operation::first_argument_type& __x) const
      { return op(__x, value); }
    } _GLIBCXX_DEPRECATED;

binder1st 不同的是,binder2nd 的 op 参数顺序反过来了,op(x, target)binder1stop(target, x)

3、mem_fun

适配对象成员函数

struct Employee
{
    Employee() {}
    Employee(const string& str, const int age) : m_name(str), m_age(age) {}

    bool operator<(const struct Employee& right) const
    {
        return m_age < right.m_age;
    }
    
    void print()
    {
        cout << m_name << " " << m_age << endl;
    }

    string m_name;
    int m_age;
};

int main()
{
    vector<Employee> emp{Employee("wxc", 22), Employee("hc", 28)};

    for_each(emp.begin(), emp.end(), &Employee::print); // error

    return 0;
}

我们先来看一下 for_each 的实现:

	template<typename _InputIterator, typename _Function>
	_Function
	for_each(_InputIterator __first, _InputIterator __last, _Function __f)
	{
		for (; __first != __last; ++__first)
			__f(*__first);
		return __f; 
	}

由于 for_each的调用方式为 _f(*first) ,我们传入的函数指针是类的成员函数不能采用该方法调用,所以就需要一个适配器。这个适配器一定可以将 fun(obj) 的调用形式转变为 pobj->fun()obj.fun()

然后我们来看 men_fun_ref 的实现:

template<typename _Ret, typename _Tp>
inline mem_fun_ref_t<_Ret, _Tp>
mem_fun_ref(_Ret (_Tp::*__f)())
{ return mem_fun_ref_t<_Ret, _Tp>(__f); }

调用 mem_fun_ref 时,传入一个函数指针,返回值为 _Ret,参数为空。类类型为 _Tp

mem_fun_ref_t 实现:

template<typename _Ret, typename _Tp>
class mem_fun_ref_t : public unary_function<_Tp, _Ret>
{
    public:
    explicit
        mem_fun_ref_t(_Ret (_Tp::*__pf)())
        : _M_f(__pf) { }

    _Ret
    operator()(_Tp& __r) const
    { return (__r.*_M_f)(); }

    private:
    _Ret (_Tp::*_M_f)();
};

mem_fun 调用函数的方式则是通过指针,其他相同:

    template<typename _Ret, typename _Tp>
    class mem_fun_t : public unary_function<_Tp*, _Ret>
    {
    public:
        explicit
        mem_fun_t(_Ret (_Tp::*__pf)())
                : _M_f(__pf) { }

        _Ret
        operator()(_Tp* __p) const
        { return (__p->*_M_f)(); }

    private:
        _Ret (_Tp::*_M_f)();
    };

    /// One of the @link memory_adaptors adaptors for member
    /// pointers@endlink.
    template<typename _Ret, typename _Tp>
    class const_mem_fun_t : public unary_function<const _Tp*, _Ret>
    {
    public:
        explicit
        const_mem_fun_t(_Ret (_Tp::*__pf)() const)
                : _M_f(__pf) { }

        _Ret
        operator()(const _Tp* __p) const
        { return (__p->*_M_f)(); }

    private:
        _Ret (_Tp::*_M_f)() const;
    };

    /// One of the @link memory_adaptors adaptors for member
    /// pointers@endlink.
    template<typename _Ret, typename _Tp>
    class mem_fun_ref_t : public unary_function<_Tp, _Ret>
    {
    public:
        explicit
        mem_fun_ref_t(_Ret (_Tp::*__pf)())
                : _M_f(__pf) { }

        _Ret
        operator()(_Tp& __r) const
        { return (__r.*_M_f)(); }

    private:
        _Ret (_Tp::*_M_f)();
    };

    /// One of the @link memory_adaptors adaptors for member
    /// pointers@endlink.
    template<typename _Ret, typename _Tp>
    class const_mem_fun_ref_t : public unary_function<_Tp, _Ret>
    {
    public:
        explicit
        const_mem_fun_ref_t(_Ret (_Tp::*__pf)() const)
                : _M_f(__pf) { }

        _Ret
        operator()(const _Tp& __r) const
        { return (__r.*_M_f)(); }

    private:
        _Ret (_Tp::*_M_f)() const;
    };

    /// One of the @link memory_adaptors adaptors for member
    /// pointers@endlink.
    template<typename _Ret, typename _Tp, typename _Arg>
    class mem_fun1_t : public binary_function<_Tp*, _Arg, _Ret>
    {
    public:
        explicit
        mem_fun1_t(_Ret (_Tp::*__pf)(_Arg))
                : _M_f(__pf) { }

        _Ret
        operator()(_Tp* __p, _Arg __x) const
        { return (__p->*_M_f)(__x); }

    private:
        _Ret (_Tp::*_M_f)(_Arg);
    };

    /// One of the @link memory_adaptors adaptors for member
    /// pointers@endlink.
    template<typename _Ret, typename _Tp, typename _Arg>
    class const_mem_fun1_t : public binary_function<const _Tp*, _Arg, _Ret>
    {
    public:
        explicit
        const_mem_fun1_t(_Ret (_Tp::*__pf)(_Arg) const)
                : _M_f(__pf) { }

        _Ret
        operator()(const _Tp* __p, _Arg __x) const
        { return (__p->*_M_f)(__x); }

    private:
        _Ret (_Tp::*_M_f)(_Arg) const;
    };

    /// One of the @link memory_adaptors adaptors for member
    /// pointers@endlink.
    template<typename _Ret, typename _Tp, typename _Arg>
    class mem_fun1_ref_t : public binary_function<_Tp, _Arg, _Ret>
    {
    public:
        explicit
        mem_fun1_ref_t(_Ret (_Tp::*__pf)(_Arg))
                : _M_f(__pf) { }

        _Ret
        operator()(_Tp& __r, _Arg __x) const
        { return (__r.*_M_f)(__x); }

    private:
        _Ret (_Tp::*_M_f)(_Arg);
    };

    /// One of the @link memory_adaptors adaptors for member
    /// pointers@endlink.
    template<typename _Ret, typename _Tp, typename _Arg>
    class const_mem_fun1_ref_t : public binary_function<_Tp, _Arg, _Ret>
    {
    public:
        explicit
        const_mem_fun1_ref_t(_Ret (_Tp::*__pf)(_Arg) const)
                : _M_f(__pf) { }

        _Ret
        operator()(const _Tp& __r, _Arg __x) const
        { return (__r.*_M_f)(__x); }

    private:
        _Ret (_Tp::*_M_f)(_Arg) const;
    };

    // Mem_fun adaptor helper functions.  There are only two:
    // mem_fun and mem_fun_ref.
    template<typename _Ret, typename _Tp>
    inline mem_fun_t<_Ret, _Tp>
    mem_fun(_Ret (_Tp::*__f)())
    { return mem_fun_t<_Ret, _Tp>(__f); }

    template<typename _Ret, typename _Tp>
    inline const_mem_fun_t<_Ret, _Tp>
    mem_fun(_Ret (_Tp::*__f)() const)
    { return const_mem_fun_t<_Ret, _Tp>(__f); }

    template<typename _Ret, typename _Tp>
    inline mem_fun_ref_t<_Ret, _Tp>
    mem_fun_ref(_Ret (_Tp::*__f)())
    { return mem_fun_ref_t<_Ret, _Tp>(__f); }

    template<typename _Ret, typename _Tp>
    inline const_mem_fun_ref_t<_Ret, _Tp>
    mem_fun_ref(_Ret (_Tp::*__f)() const)
    { return const_mem_fun_ref_t<_Ret, _Tp>(__f); }

    template<typename _Ret, typename _Tp, typename _Arg>
    inline mem_fun1_t<_Ret, _Tp, _Arg>
    mem_fun(_Ret (_Tp::*__f)(_Arg))
    { return mem_fun1_t<_Ret, _Tp, _Arg>(__f); }

    template<typename _Ret, typename _Tp, typename _Arg>
    inline const_mem_fun1_t<_Ret, _Tp, _Arg>
    mem_fun(_Ret (_Tp::*__f)(_Arg) const)
    { return const_mem_fun1_t<_Ret, _Tp, _Arg>(__f); }

    template<typename _Ret, typename _Tp, typename _Arg>
    inline mem_fun1_ref_t<_Ret, _Tp, _Arg>
    mem_fun_ref(_Ret (_Tp::*__f)(_Arg))
    { return mem_fun1_ref_t<_Ret, _Tp, _Arg>(__f); }

    template<typename _Ret, typename _Tp, typename _Arg>
    inline const_mem_fun1_ref_t<_Ret, _Tp, _Arg>
    mem_fun_ref(_Ret (_Tp::*__f)(_Arg) const)
    { return const_mem_fun1_ref_t<_Ret, _Tp, _Arg>(__f); }

最多只能带一个参数!

七 内存分配器 allocator

这部分内容在另一个博客详细讲解

八 其他

  • 效率比手写循环高
  • 代码可读性,可维护性更高

注意,下面这样写是不行的:

emp.swap(vector<Employee>()); // error
//! Non-const lvalue reference to type 'vector<...>' cannot bind to a temporary of type 'vector<...>'

vector<Employee>().swap(emp); // ok 

我们来看一下 vector::swap() 的声明:

void swap(vector& __x) _GLIBCXX_NOEXCEPT

swap 参数为非 const 引用类型,第一种写法临时对象会作为参数。临时对象的引用得用 const 引用,所以只能用临时对象左右函数的第一参数,位于成员调用符号左侧

swap 函数的本质时交换了 vector 内的三个指针

void
swap(vector& __x) _GLIBCXX_NOEXCEPT
{
    this->_M_impl._M_swap_data(__x._M_impl);
    
    _Alloc_traits::_S_on_swap(_M_get_Tp_allocator(),
                              __x._M_get_Tp_allocator());
}

void _M_swap_data(_Vector_impl& __x) _GLIBCXX_NOEXCEPT
{
    std::swap(_M_start, __x._M_start);
    std::swap(_M_finish, __x._M_finish);
    std::swap(_M_end_of_storage, __x._M_end_of_storage);
}