Constructors for mutable and const versions of an iterator class

According to section 3.2 of the book "Generic Programming and STL", it is recommended to define both mutable and const versions of an iterator class. However, the constructors provided in the example code are still not perfect because some cases are not covered.

Example code:


template <class Node, class Pointer, class Reference> struct node_wrap_base : public iterator<forward_iterator_tag, Node, ptrdiff_t, Poitner, Reference> {
  typedef node_wrap_base<Node, Node*, Node&> iterator;
  typedef node_wrap_base<Node, const Node*, const Node&> const_iterator;

  node_wrap_base(Pointer p = 0) : ptr(p) {}
  node_wrap_base(const iterator& x) : ptr(x.ptr) {}

  // ...
};

template <class Node> struct node_wrap : public node_wrap_base<Node, Node*, Node&> {
  typedef node_wrap_base<Node, Node*, Node&> Base;

  node_wrap(Node* p = 0) : Base(p) {}
  node_wrap(const node_wrap<Node>& x) : Base(x) {}

  // ...
};

template <class Node> struct const_node_wrap : public node_wrap_base<Node, const Node*, const Node&> {
  typedef node_wrap_base<Node, const Node*, const Node&> Base;

  const_node_wrap(const Node* p = 0) : Base(p) {}
  const_node_wrap(const node_wrap<Node>& x) : Base(x) {}

  // ...
};

For details, the constructors for the base class node_wrap_base only support the following construction modes (Here, it should be noted that we call those pointers or references as const pointers or const references, if they are related to constant objects):

  • Constructor: node_wrap_base(Pointer p = 0) : ptr(p) {}

    • Construct a mutable node_wrap_base from a mutable pointer.
    • Construct a const node_wrap_base from a const pointer.

    The derivation of the above constructors is based on the fact that node_wrap_base has the same argument type Pointer as that of the argument p.

  • Copy constructor: node_wrap_base(const iterator& x) : ptr(x.ptr) {}
    • Construct a mutable node_wrap_base from a mutable iterator by copying, which is because the iterator is defined as

      typedef node_wrap_base<Node, Node*, Node&> iterator;

      which is mutable.

    • Construct a const node_wrap_base from a mutable iterator by copying.

It can be seen that there are still two cases not considered by the example code. They are

  • Construct a const node_wrap_base from a mutable pointer.
  • Construct a const node_wrap_base from a const iterator by copying.

For the first constructor in the above, we might define it as below:


ndoe_wrap_base<Node, const Node*, const Node&>(Node* p) : ptr(p) {}

However, C++ does not allow template constructor. Therefore, we can only define it as


node_wrap_base(Node* p) : ptr(p) {}

This is still not correct. Because when the template argument Pointer passed to node_wrap_base is Node*, which also satisfies the above constructor‘s formulation, which means we have two overloaded functions with the same function signature. This is not allowed by C++. The correct way to define base class constructors is:

  • node_wrap_base(const_poitner p = 0) : ptr(p)

    • Construct a mutable iterator from a const pointer. This is allowed!
    • Construct a const iterator from a const pointer.
  • node_wrap_base(mutable_pointer p) : ptr(p)
    • Construct a mutable iterator from a mutable pointer.
    • Construct a const iterator from a mutable pointer.

It should also be noted that because there is already a default constructor as the first one above, the above second constructor should not have default argument.

For the mutable version of the child class, the two constructors defined in the example code as below can cover all possible situations:

  • node_wrap(Node* p = 0) : Base(p) {}: Construct a mutable iterator from a mutable pointer.
  • node_wrap(const node_wrap<Node>& x) : Base(x) {}: Construct a mutable iterator from a mutable iterator.

It can be seen that the constructors for mutable version of the iterator class are complete, because mutable iterators can only be constructed from mutable pointer or copied from another mutable iterator.

For the const version of the iterator, the two constructors defined in the example code are:

  • const_node_wrap(const Node* p = 0) : Base(p) {}: construct a const iterator from a const pointer.
  • const_node_wrap(const node_wrap<Node>& x) : Base(x) {}: construct a const iterator from a mutable iterator by copying.

There are still two cases not considered by the example code:

  • Construct a const iterator from a mutable pointer.
  • Construct a const iterator from a const iterator by copying.

They can be defined as below:

  • const_node_wrap(Node* p = 0) : Base (p) {}
  • const_node_wrap(const const_node_wrap<Node>& i) : ptr(i.ptr) {}

In this way, all the constructors are correctly created for mutable and const versions of iterator classes. The complete code is listed as below.


// This example code demonstrates how to define constructors for mutable and
// const versions of an iterator class.

#include <iostream>
#include <ostream>
#include <iterator>
#include <algorithm>

using namespace std;

template <class Node> struct const_node_wrap;

// Integer linked list node.
struct int_node {
  // Overload << operator
  // Note: the << operator has two arguments with the first one as the ostream
  // object, therefore it cannot be a member function of int_node.
  friend ostream& operator<<(ostream& os, const struct int_node & i);

  // Constructor
  int_node(int v = 0, int_node* p = NULL) : val(v), next(p) {}
  // Copy constructor
  int_node(const int_node& i) : val(i.val), next(i.next) {}

  // Assignment from an int_node struct. After the assignment, the new value
  // should be comparable to the original one.
  int_node& operator=(const int_node& i) {
    val = i.val;
    next = i.next;

    return *this;
  }

  // Only assign value.
  int_node& operator=(int i) {
    val = i;

    return *this;
  }

  // Equality operator.
  bool operator==(const int_node& i) {return val == i.val && next == i.next;}
  bool operator==(const int i) {return val == i;}
  bool operator!=(const int_node& i) {return val != i.val || next != i.next;}
  bool operator!=(const int i) {return val != i;}

  // Sum operator.
  int_node operator+(const int_node& i) {
    int_node sum_node;
    sum_node.val = this->val + i.val;
    return sum_node;
  }

  int_node& operator+=(const int_node& i) {
    this->val += i.val;
    return *this;
  }

  int val;
  int_node* next;
};

ostream& operator<<(ostream& os, const int_node & i) {
  return os << i.val;
}

template <class Node, class Pointer, class Reference> struct node_wrap_base : public iterator<forward_iterator_tag, Node, ptrdiff_t, Pointer, Reference> {
  typedef Node* mutable_pointer;
  typedef const Node* const_pointer;
  typedef Node& mutable_reference;
  typedef const Node& const_reference;
  typedef node_wrap_base<Node, mutable_pointer, mutable_reference> iterator;
  typedef node_wrap_base<Node, const_pointer, const_reference> const_iterator;

  Pointer ptr;

  //! Default constructor and constructor from a pointer, which can be mutable or
  //! const.
  //! If it is to construct a mutable iterator from a const pointer, which is
  //! invalid. The only situation is construct a const iterator from a const
  //! pointer.
  node_wrap_base(const_pointer p = 0) : ptr(p) { cout << "    *node_wrap_base construct from const pointer" << endl; }
  //! Default constructor and constructor from a mutable pointer.
  //! It includes two situations:
  //! - Construct a mutable iterator from a mutable pointer.
  //! - Construct a const iterator from a mutable pointer.
  //! mutable pointer.
  node_wrap_base(mutable_pointer p) : ptr(p) { cout << "    *node_wrap_base construct from mutable pointer" << endl; }

  //! Copy constructor
  //! Copy construct a const iterator from a const iterator, while the case for
  //! copy construct a mutable iterator from a const iterator is invalid.
  node_wrap_base(const const_iterator& i) : ptr(i.ptr) { cout << "    *node_wrap_base copy construct from const_iterator" << endl; }

  //! Copy construct
  //! Copy construct an iterator, either mutable or const, from a mutable
  //! iterator.
  //! It includes two situations:
  //! - Copy construct a mutable iterator from a mutable iterator.
  //! - Copy construct a const iterator from a mutable iterator.
  node_wrap_base(const iterator& i) : ptr(i.ptr) { cout << "    *node_wrap_base copy construct from mutable iterator" << endl; }

  //! Dereference operator
  Reference operator*() const { return *ptr; }
};

// Mutable wrapper class for the linked list node, which plays the role of iterator.
template <class Node> struct node_wrap : public node_wrap_base<Node, Node*, Node&> {
  typedef node_wrap_base<Node, Node*, Node&> Base;

  //! Constructor
  node_wrap(Node* p = 0) : Base(p) { cout << "    *node_wrap construct from mutable pointer" << endl; }
  //! Copy constructor
  node_wrap(const node_wrap<Node>& i) : Base(i) { cout << "    *node_wrap copy construct from mutable iterator" << endl; }
};

//! Const wrapper class for the linked list node, which plays the role of iterator.
template <class Node> struct const_node_wrap : public node_wrap_base<Node, const Node*, const Node&> {
  typedef node_wrap_base<Node, const Node*, const Node&> Base;

  //! Constructor
  const_node_wrap(Node* p = 0) : Base(p) { cout << "    *const_node_wrap construct from mutable pointer" << endl; }
  const_node_wrap(const Node* p) : Base(p) { cout << "    *const_node_wrap construct from const pointer" << endl; }

  //! Copy constructor
  const_node_wrap(const node_wrap<Node>& i) : Base(i) { cout << "    *const_node_wrap copy construct from mutable iterator" << endl; }
  const_node_wrap(const const_node_wrap<Node>& i) : Base(i) { cout << "    *const_node_wrap copy construct from const iterator" << endl; }
};

int main() {
  const int array_length = 10;
  int_node int_list[array_length];
  const int_node * int_list_const = int_list;

  cout << "Test constructors...\n";

  cout << ">>> Construct a mutable iterator from a mutable pointer." << endl;
  node_wrap<int_node> mutable_iter1(int_list);
  *mutable_iter1 = int_node(1);

  cout << ">>> Construct a const iterator from a mutable pointer." << endl;
  const_node_wrap<int_node> const_iter1(int_list);
  // *const_iter1 = int_node(5);   //! This should fail.

  cout << ">>> Construct a const iterator from a const pointer." << endl;
  const_node_wrap<int_node> const_iter2(int_list_const);
  // *const_iter2 = int_node(1); //! This should fail.

  //! cout << ">>> Construct a mutable iterator from a const pointer." << endl;
  //! node_wrap<int_node> const_iter3(int_list_const); //! This should fail.

  cout << "Test copy constructors...\n";

  cout << ">>> Copy construct a mutable iterater from a mutable iterator." << endl;
  node_wrap<int_node> mutable_iter2(mutable_iter1);
  *mutable_iter2 = int_node(1);

  cout << ">>> Copy construct a const iterator from a mutable iterator." << endl;
  const_node_wrap<int_node> const_iter4(mutable_iter1);
  // *const_iter4 = int_node(1); //! This should fail.

  cout << ">>> Copy construct a const iterator from a const iterator." << endl;
  const_node_wrap<int_node> const_iter5(const_iter1);
  // *const_iter5 = int_node(1);   //! This should fail.

  //! cout << ">>> Copy construct a mutable iterator from a cosnt iterator." << endl;
  //! node_wrap<int_node> mutable_iter3(const_iter1); //! This should fail.

  return 0;
}
时间: 2024-10-25 23:30:36

Constructors for mutable and const versions of an iterator class的相关文章

const对象,NULL和nullptr,C++中创建对象数组

 1.定义成了const之后的类 #include <iostream> class area { public: int x; int y; mutable int z; //不受const约束的类成员 area() :x(10), y(10), z(2) { } void printxy()const //不可以访问类中局部变量 { z = z + 1; std::cout << x << " " << y << &q

不可或缺 Windows Native (18) - C++: this 指针, 对象数组, 对象和指针, const 对象, const 指针和指向 const 对象的指针, const 对象的引用

[源码下载] 不可或缺 Windows Native (18) - C++: this 指针, 对象数组, 对象和指针, const 对象,  const 指针和指向 const 对象的指针, const 对象的引用 作者:webabcd 介绍不可或缺 Windows Native 之 C++ this 指针 对象数组 对象和指针 const 对象 const 指针和指向 const 对象的指针 const 对象的引用 示例1.CppEmployee 类CppEmployee.h #pragma

Const member functions in C++

Recently, I've started to review and learn C++ techniques. During the learning process, I followed a learn-by-example methodology, which I consider to be very effective. From now on, I'll use this blog to explain key points and interesting topics in

Effective C++ 之 Item 3:尽可能使用 const

Effective C++ Chapter 1. 让自己习惯C++(Accustoming Yourself to C++) Item 3. 尽可能使用 const (Use const whenever possible) 1. const 与语义约束 const 允许指定一个语义约束(也就是指定一个"不该被改动"的对象),而编译器强制实施这项约束.它可以在 classes 外部修饰 global 或 namespace(见 Item2)作用域中的常量,或修饰文件.函数.或区块作用域

C++要点(1)mutable关键字

mutable语义 const语义 mutable和const的关系 mutable提供的灵活性 mutable语义 在C++中,mutable是为了突破const的限制而设置的. 被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中,甚至结构体变量或者类对象为const,其mutable成员也可以被修改. 例如: #include <iostream> class Tester { public: Tester(int x, int y) : a_normal(x),

第二篇:尽可能使用 const

前言 const 关键字是常量修辞符,如果要告知编译器某个变量在程序中不会发生改变,则可将其声明为 const. 但,对 const 关键字的认识不能仅仅停留在这一层 - 它提供了很多更强大的功能. 因此很多情况下 const 关键字的使用方法也不是简单的 const + 变量,其用法总的来说是比较灵活的. 指针的 const 关键字 const 关键字出现在 * 左边或是右边定义出来的指针完全不同. 1 const int *pa = &a; 这样的一行代码定义了一个指向整型变量 a 的指针

尽可能使用 const

前言 const 关键字是常量修辞符,如果要告知编译器某个变量在程序中不会发生改变,则可将其声明为 const. 但,对 const 关键字的认识不能仅仅停留在这一层 - 它提供了很多更强大的功能. 因此很多情况下 const 关键字的使用方法也不是简单的 const + 变量,其用法总的来说是比较灵活的. 指针的 const 关键字 const 关键字出现在 * 左边或是右边定义出来的指针完全不同. 1 const int *pa = &a; 这样的一行代码定义了一个指向整型变量 a 的指针

Effective C++ Item 3 尽可能使用const

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie const 出现在*左边指const data,出现在*右边指const pointer char greeting[] = "Hello"; char *p = greeting; //non-const pointer, non-const data const char *p = greeting; //non-const pointer, const data char

c++笔记:const、初始化、copy构造/析构/赋值函数

构造函数 Default构造函数:可被调用而不带任何实参的构造函数,没有参数或每个参数都有缺省值.如: class A { public: A(); }; 将构造函数声明为explicit,可阻止它们被用来执行隐式类型转换,但仍可用来进行显示类型转换.如: class B { public: explicit B(int x = 0, bool b = ture); }; copy构造函数:用于以同型对象初始化自我对象,以passed by value的方式传递对象:· copy assignm