template <typename Iter> void my_swap(Iter a, Iter b) { tmp = *a;//error -- variable tmp undeclared *a = *b; *b = tmp; }函数模板的实例用来交换迭代器参数所指向的两个对象:a 和 b。dmp 应该是什么类型我们没法知道,我们知道迭代器指向对象的类型却无计可施,因为直到类模板生成实例时,才能确定对象的类型。在不知道对象的类型时,如何定义变量?当然,这里可以使用 auto。在一些情况下,我们也想知道迭代器类型的值和类型差别。
template <typename Iter> void my_swap(Iter a, Iter b) { typename Iter::value—type tmp = *a; *a = *b; *b = tmp; }因为 value_type 的别名定义在 Iter 类中,所以可以通过用类名限定 value_type 的方式引用它。这样定义了 value_type 别名的迭代器类就能在函数中正常使用。然而,算法既使用指针,也使用迭代器;如果 Iter 是普通类型的指针,例如 int*,甚至是 Box*,而 Box 是类型一这样可能就无法使用了。因为指针不是类,不能包含定义的别名,所以不能写成 int*::value_type 或 Box*::value_type。STL 用模板优雅地解决了这个问题和其他一些相关问题!
template<class Iterator> struct iterator_traits { typedef typename Iterator::difference_type difference_type; typedef typename Iterator::value_type value_type; typedef typename Iterator::pointer pointer; typedef typename Iterator::reference reference; typedef typename Iterator::iterator_category iterator_category; };
template <typename Iter> void my_swap(Iter a, Iter b) { typename std::iterator_traits<Iter>::value_type tmp = *a; *a = *b; *b = tmp; }上述代码将 tmp 的类型指定为 iterator_traits 模板中的 value_type 别名。当用 Iter 模板参数实例化 my_swap() 模板时,tmp 的类型变为迭代器所指向的类型 Iter::value_type。
std::vector<std::string> words {"one", "two", "three"}; my_swap(std::begin(words), std::begin(words)+1); //Swap first two elements当编译器遇到 my_swap() 调用时,它会生成一个基于调用参数的函数模板实例。模板类型的迭代器是 iterator<std::string>。在 my_swap() 模板的主体中,编译器不得不处理 tmp 的定义,编译器知道模板的类型参数是 iterator<std::string>,因此在向模板添加了这个类型后,tmp 的定义变为:
typename std::iterator_traits<iterator<std::string> >::value_type tmp = *a;tmp 的类型现在是一个 iterator_traits 模板实例的成员变量。为了弄清楚这意味着什么,编译器需要使用 my_swap() 函数中用来指定 tmp 类型的类型参数来实例化 iterator_traits 模板。下面是一个编译器将会生成的 iterator_traits 模板实例:
struct iterator—traits { typedef typename iterator<std::string>::difference_type difference_type; typedef typename iterator<std::string>::value_type value_type; typedef typename iterator<std::string>::pointer pointer; typedef typename iterator<std::string>::reference reference; typedef typename iterator<std:: string>:: iterator_category iterator_category; };从这里编译器可以确定 tmp 的类型为 iterator_traits<iterator<std::string>>::value_type,然而它也是 iterator<std::string>::value_type 的别名。就像所有的 STL 迭代器类型,iterator<std::string> 类型的定义是从 iterator 模板中生成的,并且会包含 value_type 的定义, 看起来像下面这样:
typedef std::string value_type;现在编译器从 iterator_traits 实例中知道 itemtor_traits<itemtor<std::string>>::value_type 是 iterator<std::string>::value_type 的别名,并且从 itemtor<std::string> 类定义中知道 iterator<std::string>:: value_type 是 std::string 的别名。通过将别名转换为真实类型,编译器推断出 my_swap() 函数中 tmp 的定义是:
std::string tmp = *a;有必要提醒自己模板不是代码一它是编译器用来生成代码的配方。iterator_traits 模板只包含类型别名,因此不会产生可执行代码。编译器在生成 C++ 代码的过程中,会用到它。被编译的代码中将不会有 iterator_traits 模板的踪迹;它的唯一用武之地是在生成 C++ 代码的过程中。
template<class T> struct iterator_traits<T*> { typedef ptrdiff_t difference_type; typedef T value_type; typedef T* pointer; typedef T& reference; typedef random_access_iterator_tag iterator_category; };当模板类型参数是指针类型时,这定义了对应于别名的类型。T* 类型的指针 value_type 的别名总是为 T;如果将 Box* 类型的指针作为 my_swap() 的参数,那么 value_type 的别名是 Box,因此 tmp 也为这种类型。
template<class Category, class T, class Difference = ptrdiff_t, class Pointer =T*,class Reference = T&> struct iterator { typedef T value_type; typedef Difference difference_type; typedef Pointer pointer; typedef Reference reference; typedef Category iterator_category };这个模板定义了 STL 对迭代器所要求的全部类型。例如,如果有一个未知类型的模板参数 Iter,当需要声明一个指针时,它指向一个迭代器解引用时提供的类型,这时可以写作 Iter::pointer。 iterator_category 的值必定是在前面章节介绍的类别标签类中的一个。
class My_Iterator : public std::iterator<std::random_access_iterator_tag,int> { // Members of the iterator class... };还需要注意,需要为迭代器定义 STL 要求的全部类型。模板的第 1 个参数指定了作为完全随机访问迭代器的迭代器类型。第 2 个参数是迭代器所指向对象的类型。最后的 3 个参数是默认值,因此第 3 个参数和这两个迭代器的类型不同,是 ptrdiff_t。第 4 个参数是一个指向对象的指针类型,因此是 int*。最后一个模板参数指定了引用的类型,是 int&。当然,迭代器类型不做任何事,仍然需要定义类的全部成员。
Iterator(); // Default constructor Iterator (const Iterator& y) ; // Copy constructor ~Iterator (); // Destructor Iterator& operator= (const Iterator& y) ;// Assignment operator对于随机访问迭代器类,STL 需要一整套的关系运算符。事实上,可以通过使用 utility 标准库头文件中的函数模板来完成这些定义:
bool operator==(const Iterator& y) const; bool operator<(const Iterator& y) const;下面假定已经直接 #include 头文件 utility,并且直接使用命名空间 std::relops:
#include <utility> using namespace std::rel_ops;如果为类定义了 operator=() 和 operator<(),然后 std 中声明的命名空间 rel_ops 在必要时,就可以包含我们为 !=、>、>= 和 <= 生成的运算符函数的函数模板。因此直接用 using 激活 std::rel_ops,就可以显式保存这 4 个运算符函数。如果定义一个运算符函数,但是它已经被命名空间 std::rd_ops 模板生成,那么我们的实现的优先级高于模板生成的实现。函数 operator() 比较特别,叫作顺序关系,它对搜索和比较算法很重要。
template <typename T> class Numeric_Iterator; // Template type declaration // Defines a numeric range template<typename T> class Numeric_Range { static_assert(std::is_integral<T>::value || std::is_floating_point<T>::value, "Numeric_Range type argument must be numeric."); friend class Numeric_Iterator < T >; protected: T start; // First value in the range T step; // Increment between successive values size_t count; // Number of values in the range public: Numeric_Range(T first=0, T incr=1, size_t n=2) : start {first}, step {incr}, count {n}{} // Return the begin iterator for the range Numeric_Iterator<T> begin(){ return Numeric_Iterator<T>(*this); } // Return the end iterator for the range Numeric_Iterator<T> end() { Numeric_Iterator<T> end_iter(*this); end_iter.value = start + count*step; // End iterator value is one step over the last return end_iter; } };类型参数 T 是序列的值类型,因此它必定是数值类型。对于模板主体中的函数 static_assert(),当 T 不是整型也不是浮点型时,它的第一个参数会为 false,这时会生成一条包含第二个字符串参数的编译时错误消息。这里使用的断言模板定义在头文件 type_traits 中,模板中还有一些其他的编译时模板类型参数检查断言。这个构造函数的三个参数都有默认值,因此它也可以作为无参构造函数。这三个参数分别用来初始化值、指定一个值到另一个值的增量,以及指定值的个数。因此默认定义了又有两个值的元素段:0 和 1。编译器会在需要时,提供适当的拷贝构造函数。
// Iterator class template- it's a forward iterator template<typename T> class Numeric_Iterator : public std::iterator < std::forward_iterator_tag, T > { friend class Numeric_Range < T >; protected: Numeric_Range<T>& range; // Reference to the range for this iterator T value; // Value pointed to public: explicit Numeric_Iterator(Numeric_Range<T>& a_range) : range {a_range}, value {a_range.start} {}; // Assignment operator Numeric_Iterator& operator=(const Numeric_Iterator& src) { range = src.range; value = src.value; } // Dereference an iterator T& operator*() { // When the value is one step more than the last, it's an end iterator if (value == static_cast<T>(range.start + range.count*range.step)) { throw std::logic_error("Cannot dereference an end iterator."); } return value; } // Prefix increment operator Numeric_Iterator& operator++() { // When the value is one step more than the last, it's an end iterator if (value == static_cast<T>(range.start + range.count*range.step)) { throw std::logic_error("Cannot increment an end iterator."); } value += range.step; // Increment the value by the range step return *this; } // Postfix increment operator Numeric_Iterator operator++(int) { // When the value is one step more than the last, it's an end iterator if (value == static_cast<T>(range.start + range.count*range.step)) { throw std::logic_error("Cannot increment an end iterator."); } auto temp = *this; value += range.step; // Increment the value by the range step return temp; // The iterator before it's incremented } // Comparisons bool operator<(const Numeric_Iterator& iter) const { return value < iter.value; } bool operator==(const Numeric_Iterator& iter) const { return value == iter.value; } bool operator!=(const Numeric_Iterator& iter) const { return value != iter.value; } bool operator>(const Numeric_Iterator& iter) const { return value > iter.value; } bool operator<=(const Numeric_Iterator& iter) const { *this < iter || *this == iter; } bool operator>=(const Numeric_Iterator& iter) const { *this > iter || *this == iter; } }; #endif代码看起来虽多,却很简单直白。这个迭代器有一个成员变量,它保存了一个和它相关联的 Numeric_Range 对象的引用,另外还保存了它所指向元素的值。迭代器的构造函数的参数是一个 Numeric_Range 对象的引用。构造函数用参数初始化成员变量 range,并将成员变量 value 的值设为 Numeric_Range 的 start。
// Exercising the Numeric_Range template #include <algorithm> // For copy() #include <numeric> // For accumulate() #include <iostream> // For standard streams #include <vector> // For vector container #include "Numeric_Range.h" // For Numeric_Range<T> & Numeric_Iterator<T> int main() { Numeric_Range<double> range {1.5, 0.5, 5}; auto first = range.begin(); auto last = range.end(); std::copy(first, last, std::ostream_iterator<double>(std::cout, " ")); std::cout << "\nSum = " << std::accumulate(std::begin(range), std::end(range), 0.0) << std::endl; // Initializing a container from a Numeric_Range Numeric_Range<long> numbers {15L, 4L, 10}; std::vector<long> data {std::begin(numbers), std::end(numbers)}; std::cout << "\nValues in vector are:\n"; std::copy(std::begin(data), std::end(data), std::ostream_iterator<long>(std::cout, " ")); std::cout << std::endl; // List the values in a range std::cout << "\nThe values in the numbers range are:\n"; for (auto n : numbers) std::cout << n << " "; std::cout << std::endl; }运行结果为:
本文链接:http://task.lmcjl.com/news/16959.html