프로그래밍 언어2016. 8. 25. 15:46

뭔가 복잡하고 완벽하게 만들려면 좀더 까다롭겠지만,

range-based for loop 또는 std:find 등의 STL algorithm을 이용할 수 있도록 간단하게 만든다면, 아래와 같이 하면 되겠다.

(물론 검증된 것은 아니고, 정말 연습용으로 해 보고 정리하는 것임. 따라서 오류가 있을수도...;;)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
template <typename T>
class slist_iterator
{
public:
    typedef std::forward_iterator_tag iterator_category;
    typedef typename slist<T>::value_type value_type;             //T
    typedef typename slist<T>::reference reference;               //T&
    typedef typename slist<T>::pointer pointer;                   //T*
    typedef typename slist<T>::difference_type difference_type;   //std::ptrdiff_t
 
public:
    slist_iterator(typename slist<T>::node_ptr p = nullptr) : current_(p) {}
    ~slist_iterator() = default;
    slist_iterator(slist_iterator const&) = default;
    slist_iterator& operator=(slist_iterator const&) = default;
 
    bool operator==(slist_iterator const& rhs) const { return rhs.current_ == current_; }
    bool operator!=(slist_iterator const& rhs) const { return rhs.current_ != current_; }
    reference operator*() const { return current_->data_; }
    pointer operator->() const { return ¤t_->data_; }
 
    slist_iterator<T>& operator++() {
        current_ = current_->next_;
        return *this;
    }
 
private:
    typename slist<T>::node_ptr current_;
};


template <typename T> class slist는 이미 정의가 되어 있고,

template <typename T> class slist_iterator가 friend로 지정되어 있다.

(또는 slist의 nested class로 구현하여도 되겠다)


처음에는 아래와 같은 type을 노출하지 않았었다.

1
2
3
4
5
typedef std::forward_iterator_tag iterator_category;
typedef typename slist<T>::value_type value_type;             //T
typedef typename slist<T>::reference reference;               //T&
typedef typename slist<T>::pointer pointer;                   //T*
typedef typename slist<T>::difference_type difference_type;   //std::ptrdiff_t


그 상태로 range-based for loop은 문제가 없었지만, std::find를 호출 했더니, iterator_category를 지정해야 한다는 에러 메시지가 나와서 추가 해 주었다.


그랬더니 std::iterator_traits 뭐라뭐라 에러가 나는데, 뭐라 하는지 전혀 모르겠더라;;


그래서 혹시나 하는 마음에 value_type, reference를 추가 해 보았으나 동일ㅠㅠ


결국, stackoverflow에서 해답을 찾았다. (오늘 알게 된 사실인데, 해당 글의 const_iterator 구현 부분에서 operator*, operator->의 반환값이 그냥 reference, pointer로 되어 있어, 값을 할당하여도 에러가 발생하지 않는다. 이 부분은 수정이 필요함. 맨 아래 전체 소스 참고.)


위의 네 개의 타입과 iterator_category는 반드시 노출되어야 하고,

iterator_category에 따라 필요한 멤버함수들을 구현 해 주면 된다.


정말 간단하게 구현하는 방법은 std::iterator를 상속하여 구현하는 것인데,

이놈이 C++17에서 없어질 예정이므로, 이후 어떻게 해야 할지는 알아봐야 할듯.


아래는, 허접하지만 const_iterator까지 구현한 전체 소스 (__)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include <iterator>
#include <iostream>
#include <algorithm>
 
template <typename T>
class slist_iterator;
template <typename T>
class slist_const_iterator;
 
template <typename T>
class slist
{
    template <typename T>
    struct node {
        T data_;
        node* next_;
        node(T const& data, node* node)
            : data_(data), next_(node) {}
    };
    typedef node<T>* node_ptr;
    typedef slist_iterator<T> iterator;
    typedef slist_const_iterator<T> const_iterator;
 
public:
    typedef T value_type;
    typedef T& reference;
    typedef T const& const_reference;
    typedef T* pointer;
    typedef T const* const_pointer;
    typedef std::ptrdiff_t difference_type;
    typedef std::size_t size_type;
 
public:
    slist() : head_(nullptr) {}
    ~slist() {
        node_ptr n = head_;
        while (n && n->next_) {
            node_ptr t = n;
            n = n->next_;
            delete t;
        }
        delete n;
    }
    void push_front(T const& data) {
        head_ = new node<T>(data, head_);
    }
    T const& front() const { return head_->data_; }
    T& front() { return head_->data_; }
    void pop_front() {
        node_ptr n = head_;
        head_ = head_->next_;
        delete n;
    }
    iterator begin() { return iterator(head_); }
    const_iterator begin() const { return const_iterator(head_); }
    const_iterator cbegin() const { return const_iterator(head_); }
 
    iterator end() { return iterator(); }
    const_iterator end() const { return const_iterator(); }
    const_iterator cend() const { return const_iterator(); }
 
private:
    node_ptr head_;
 
private:
    friend class slist_iterator<T>;
    friend class slist_const_iterator<T>;
};
 
template <typename T>
class slist_iterator
{
public:
    typedef std::forward_iterator_tag iterator_category;
    typedef typename slist<T>::value_type value_type;
    typedef typename slist<T>::reference reference;
    typedef typename slist<T>::pointer pointer;
    typedef typename slist<T>::difference_type difference_type;
 
public:
    slist_iterator(typename slist<T>::node_ptr p = nullptr) : current_(p) {}
    ~slist_iterator() = default;
    slist_iterator(slist_iterator const&) = default;
    slist_iterator& operator=(slist_iterator const&) = default;
 
    bool operator==(slist_iterator const& rhs) const { return rhs.current_ == current_; }
    bool operator!=(slist_iterator const& rhs) const { return rhs.current_ != current_; }
    reference operator*() const { return current_->data_; }
    pointer operator->() const { return ¤t_->data_; }
 
    slist_iterator<T>& operator++() {
        current_ = current_->next_;
        return *this;
    }
 
private:
    typename slist<T>::node_ptr current_;
};
 
template <typename T>
class slist_const_iterator
{
public:
    typedef typename slist<T>::value_type value_type;
    typedef typename slist<T>::reference reference;
    typedef typename slist<T>::const_reference const_reference;
    typedef typename slist<T>::pointer pointer;
    typedef typename slist<T>::const_pointer const_pointer;
    typedef typename slist<T>::difference_type difference_type;
    typedef std::forward_iterator_tag iterator_category;
 
public:
    slist_const_iterator(typename slist<T>::node_ptr p = nullptr) : current_(p) {}
    slist_const_iterator(slist_const_iterator const&) = default;
    slist_const_iterator(slist_iterator<T> const& rhs) {
        current_ = rhs.current_;
    }
    ~slist_const_iterator() = default;
    slist_const_iterator& operator=(slist_const_iterator const&) = default;
 
    bool operator==(slist_const_iterator const& rhs) { return rhs.current_ == current_; }
    bool operator!=(slist_const_iterator const& rhs) { return rhs.current_ != current_; }
    slist_const_iterator& operator++() {
        current_ = current_->next_;
        return *this;
    }
    const_reference operator*() const { return current_->data_; }
    const_pointer operator->() const { return ¤t_->data_; }
 
private:
    typename slist<T>::node_ptr current_;
};
 
int main()
{
    slist<int> list;
    for (int i = 0; i < 20; ++i) {
        list.push_front(i);
    }
    for (int i = 100; i < 120; ++i) {
        list.push_front(i);
    }
    for (auto i : list) {
        std::cout << i << '\n';
    }
 
    auto itr = std::find(list.begin(), list.end(), 100);
    while (itr != list.end()) {
        std::cout << *itr << '\n';
        ++itr;
    }
 
    for (int i = 0; i < 10; ++i) {
        list.pop_front();
    }
 
    auto citr = std::find(list.cbegin(), list.cend(), 119);
    while (citr != list.cend()) {
        std::cout << *citr << '\n';
        ++citr;
    }
 
    citr = std::find(list.cbegin(), list.cend(), 15);
    while (citr != list.cend()) {
        std::cout << *citr << '\n';
        //*citr = 100; //error!!!
        ++citr;
    }
}

Posted by 세월의돌