[lv5] ft_containers(2/6) vectorで使うサブ関数

[(1/6)vectorチュートにもどる] https://note.com/syamashi/n/n55ee0dad207d
[(3/6)vector実装にすすむ] https://note.com/syamashi/n/n329e33f09bb3

vectorの全体像が見えたところで、いったんサブ関数の再実装にうつります。

この課題を進める一番大事なテク。まず本家実装をよく読みます。
Q. 本家実装とは
A. VSCodeだと、Ctrlを押しながら変数名をクリックで、参照元にアクセスできます。

#include <type_traits>

int main(){
  std::iterator_traits  // Ctrlを押しながらアクセス
}

参照先
...\include\c++\bits\stl_iterator_base_types.h


 template<typename _Iterator>
   struct iterator_traits
   : public __iterator_traits<_Iterator> { };
#else
 template<typename _Iterator>
   struct iterator_traits
   {
     typedef typename _Iterator::iterator_category iterator_category;
     typedef typename _Iterator::value_type        value_type;
     typedef typename _Iterator::difference_type   difference_type;
     typedef typename _Iterator::pointer           pointer;
     typedef typename _Iterator::reference         reference;
   };
#endif

記載の本家コードを読み解いて実装します。

・iterators_traits

チュートリアルを読む。
https://cpp.rainy.me/031-iterator-operations.html#iterator-traits

基本方針は cppreference とします。
https://en.cppreference.com/w/cpp/iterator/iterator_traits

/utis/iterator.hpp

#ifndef ITERATOR_HPP
#define ITERATOR_HPP

#include <cstddef>

namespace ft {

//// Tags

struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};

};  // namespace ft

#endif

iteratorは必ず5種類のどれかに分類されます。そのタグを作成します。

/utis/iterator.hpp

//// iterator_traits

template <class Iterator>
struct iterator_traits {
 typedef typename Iterator::iterator_category iterator_category;
 typedef typename Iterator::value_type value_type;
 typedef typename Iterator::difference_type difference_type;
 typedef typename Iterator::pointer pointer;
 typedef typename Iterator::reference reference;
};

/// Partial specialization for pointer types.
template <class T>
struct iterator_traits<T*> {
 typedef ptrdiff_t difference_type;
 typedef T value_type;
 typedef T* pointer;
 typedef T& reference;
 typedef ft::random_access_iterator_tag iterator_category;
};

/// Partial specialization for pointer types.
template <class T>
struct iterator_traits<const T*> {
 typedef ptrdiff_t difference_type;
 typedef T value_type;
 typedef const T* pointer;
 typedef const T& reference;
 typedef ft::random_access_iterator_tag iterator_category;
};

iterator_traits は 決まった5つの Member types を必ず持ちます。

・reverse_iterator

・cppreference
https://en.cppreference.com/w/cpp/iterator/reverse_iterator

Member Types から。
std::iteratorの継承からMember Typesを受け取るように、とのことなので、ft::iteratorも仕込んでおきます。
/utis/iterator.hpp

//// other iterators

template <class Category, class T, class Distance = std::ptrdiff_t,
         class Pointer = T*, class Reference = T&>
struct iterator {
 typedef Category iterator_category;
 typedef T value_type;
 typedef Distance difference_type;
 typedef Pointer pointer;
 typedef Reference reference;
};

//// reverse_iterator

template <class Iter>
class reverse_iterator
   : public iterator<typename ft::iterator_traits<Iter>::iterator_category,
                     typename ft::iterator_traits<Iter>::value_type,
                     typename ft::iterator_traits<Iter>::difference_type,
                     typename ft::iterator_traits<Iter>::pointer,
                     typename ft::iterator_traits<Iter>::reference> {
protected:
 Iter _current;
 typedef ft::iterator_traits<Iter> traits_type;

public:
 typedef Iter iterator_type;
 typedef typename traits_type::iterator_category iterator_category;
 typedef typename traits_type::value_type value_type;
 typedef typename traits_type::difference_type difference_type;
 typedef typename traits_type::pointer pointer;
 typedef typename traits_type::reference reference;
 }
};

Member functions をうめていきます。
Constructor 3つ
/utis/iterator.hpp

class reverse_iterator{
...

 //// Member functions

 reverse_iterator() : _current() {}
 explicit reverse_iterator(iterator_type x) : _current(x) {}
 template <class U>
 reverse_iterator(const reverse_iterator<U>& other) : _current(other.base()) {}

operator=

 template <class U>
 reverse_iterator& operator=(const ft::reverse_iterator<U>& other) {
   _current = other.base();
   return *this;
 }

destructor ( coplien form にするため )

 virtual ~reverse_iterator() {}

base

 iterator_type base() const { return _current; }

operator* / operator->

 reference operator*() const {
   Iter tmp = _current;
   return *--tmp;
 }
 pointer operator->() const { return &(operator*()); }

Q. operator*() で *--tmpをかえす
A. reverse_iteratorでは、ポインタの管理と、出力するポインタに1個ずれがあります。

・ポインタ管理
rbegin は end の位置に。
rend は begin の位置に_currentが割り当てられます。

    begin                end
      0    1    2    3    X

     rend              rbegin
      0    1    2    3    X

・出力するポインタ
rbeginはendの位置にあるけど、3をかえします。
1個左を返すので、*--iter が戻り値。
なお、*rend は参照しない想定。

    begin                end
      0    1    2    3    X

rend               rbegin
 Y    0    1    2    3    X

operator[]

reference operator[](difference_type n) const { return *(*this + n); }

operators

 reverse_iterator& operator++() {
   --_current;
   return *this;
 }
 reverse_iterator& operator--() {
   ++_current;
   return *this;
 }
 reverse_iterator operator++(int) {
   reverse_iterator __tmp = *this;
   --_current;
   return __tmp;
 }
 reverse_iterator operator--(int) {
   reverse_iterator __tmp = *this;
   ++_current;
   return __tmp;
 }
 reverse_iterator operator+(difference_type n) const {
   return reverse_iterator(_current - n);
 }
 reverse_iterator operator-(difference_type n) const {
   return reverse_iterator(_current + n);
 }
 reverse_iterator& operator+=(difference_type n) {
   _current -= n;
   return *this;
 }
 reverse_iterator& operator-=(difference_type n) {
   _current += n;
   return *this;
 }

reverse_iterator非メンバ関数のオーバーロード
/utis/iterator.hpp

//// reverse_iterator_nonmember_functions

template <class Iterator1, class Iterator2>
bool operator==(const ft::reverse_iterator<Iterator1>& lhs,
               const ft::reverse_iterator<Iterator2>& rhs) {
 return lhs.base() == rhs.base();
}
template <class Iterator1, class Iterator2>
bool operator!=(const ft::reverse_iterator<Iterator1>& lhs,
               const ft::reverse_iterator<Iterator2>& rhs) {
 return !(lhs == rhs);
}

template <class Iterator1, class Iterator2>
bool operator<(const ft::reverse_iterator<Iterator1>& lhs,
              const ft::reverse_iterator<Iterator2>& rhs) {
 return rhs.base() < lhs.base();
}
template <class Iterator1, class Iterator2>
bool operator>(const ft::reverse_iterator<Iterator1>& lhs,
              const ft::reverse_iterator<Iterator2>& rhs) {
 return lhs < rhs;
}
template <class Iterator1, class Iterator2>
bool operator<=(const ft::reverse_iterator<Iterator1>& lhs,
               const ft::reverse_iterator<Iterator2>& rhs) {
 return !(rhs < lhs);
}
template <class Iterator1, class Iterator2>
bool operator>=(const ft::reverse_iterator<Iterator1>& lhs,
               const ft::reverse_iterator<Iterator2>& rhs) {
 return !(lhs < rhs);
}

template <class Iter>
ft::reverse_iterator<Iter> operator+(
   typename ft::reverse_iterator<Iter>::difference_type n,
   const ft::reverse_iterator<Iter>& it) {
 return ft::reverse_iterator<Iter>(it.base() - n);
}

template <class Iterator>
typename ft::reverse_iterator<Iterator>::difference_type operator-(
   const ft::reverse_iterator<Iterator>& lhs,
   const ft::reverse_iterator<Iterator>& rhs) {
 return rhs.base() - lhs.base();
}

const ~~ でも動かすため、非メンバを実装。
引数でft::reverse_iterator<>を呼び出す必要があり、関数宣言がとても長くなりがち。
template<>を含む関数は.hppに書いていい、というのはそこから来てるのかもしれない。
template<>の中にない関数は.cppに記述しないといけません。自分の実装ではすべて.hppで完結します。

その他関数
/utis/iterator.hpp

//// Functions

template <class InputIterator, class Distance>
void advance(InputIterator& it, Distance n) {
 while (n > 0) {
   ++it;
   --n;
 }
 while (n < 0) {
   --it;
   ++n;
 }
 return;
}

template <class InputIterator>
typename ft::iterator_traits<InputIterator>::difference_type distance(
   InputIterator first, InputIterator last) {
 typename ft::iterator_traits<InputIterator>::difference_type ret = 0;
 while (first != last) {
   ++first;
   ++ret;
 }
 return (ret);
}

・random_access_iterator

アウトラインはreverse_iterator と全く同じ。中身だけ変えます。
ft::iteratorの継承からMember Typesを受けとります。
utis/random_access_iterator.hpp

#ifndef RANDOM_ACCESS_ITERATOR_HPP
#define RANDOM_ACCESS_ITERATOR_HPP

#include "iterator.hpp"

namespace ft {
template <typename T>
class random_access_iterator
   : public ft::iterator<ft::random_access_iterator_tag, T> {

}
};  // namespace ft
#endif

5つの Member Typesを記述。
utis/random_access_iterator.hpp

protected:
 T* _current;

public:
 typedef typename ft::iterator<ft::random_access_iterator_tag,
                               T>::iterator_category iterator_category;
 typedef typename ft::iterator<ft::random_access_iterator_tag, T>::value_type
     value_type;
 typedef
     typename ft::iterator<ft::random_access_iterator_tag, T>::difference_type
         difference_type;
 typedef T* pointer;
 typedef T& reference;

Member functionをうめます。
utis/random_access_iterator.hpp

 random_access_iterator() : _current(NULL) {}
 random_access_iterator(pointer x) : _current(x) {}
 random_access_iterator(const random_access_iterator& other)
     : _current(other._current) {}
 random_access_iterator& operator=(const random_access_iterator& other) {
   if (this == &other) return (*this);
   _current = other._current;
   return (*this);
 }
 ~random_access_iterator() {}
 pointer base() const { return _current; }
 reference operator*() const { return *_current; }
 pointer operator->() const { return &(operator*()); }
 reference operator[](difference_type n) const { return *(*this + n); }
 random_access_iterator& operator++() {
   ++_current;
   return *this;
 }
 random_access_iterator& operator--() {
   --_current;
   return *this;
 }
 random_access_iterator operator++(int) {
   random_access_iterator __tmp = *this;
   ++_current;
   return __tmp;
 }
 random_access_iterator operator--(int) {
   random_access_iterator __tmp = *this;
   --_current;
   return __tmp;
 }
 random_access_iterator operator+(difference_type n) const {
   return random_access_iterator(_current + n);
 }
 random_access_iterator operator-(difference_type n) const {
   return random_access_iterator(_current - n);
 }
 random_access_iterator& operator+=(difference_type n) {
   _current += n;
   return *this;
 }
 random_access_iterator& operator-=(difference_type n) {
   _current -= n;
   return *this;
 }

 /*
 ** ex. ft::reverse_iterator<ft::vector<int>::const_iterator> it1 =
 *a1.rbegin();
 */
 operator random_access_iterator<const T>() const {
   return (random_access_iterator<const T>(_current));
 }
};

Q. operator+=
A. 
_current += n;
random_access_iteratorは要素が連続的に入っている想定のiteratorなので、ポインタの足し算で要素移動します。

非メンバ関数を埋めます。

utis/random_access_iterator.hpp

//// Non-member functions

template <class Iterator1, class Iterator2>
bool operator==(const ft::random_access_iterator<Iterator1>& lhs,
               const ft::random_access_iterator<Iterator2>& rhs) {
 return lhs.base() == rhs.base();
}
template <class Iterator1, class Iterator2>
bool operator!=(const ft::random_access_iterator<Iterator1>& lhs,
               const ft::random_access_iterator<Iterator2>& rhs) {
 return !(lhs == rhs);
}
template <class Iterator1, class Iterator2>
bool operator<(const ft::random_access_iterator<Iterator1>& lhs,
              const ft::random_access_iterator<Iterator2>& rhs) {
 return lhs.base() < rhs.base();
}
template <class Iterator1, class Iterator2>
bool operator<=(const ft::random_access_iterator<Iterator1>& lhs,
               const ft::random_access_iterator<Iterator2>& rhs) {
 return rhs < lhs;
}
template <class Iterator1, class Iterator2>
bool operator>(const ft::random_access_iterator<Iterator1>& lhs,
              const ft::random_access_iterator<Iterator2>& rhs) {
 return rhs < lhs;
}
template <class Iterator1, class Iterator2>
bool operator>=(const ft::random_access_iterator<Iterator1>& lhs,
               const ft::random_access_iterator<Iterator2>& rhs) {
 return lhs < rhs;
}

template <class Iter>
ft::random_access_iterator<Iter> operator+(
   typename ft::random_access_iterator<Iter>::difference_type n,
   const ft::random_access_iterator<Iter>& it) {
 return ft::random_access_iterator<Iter>(it.base() + n);
}

template <class Iterator>
typename ft::random_access_iterator<Iterator>::difference_type operator-(
   const ft::random_access_iterator<Iterator>& lhs,
   const ft::random_access_iterator<Iterator>& rhs) {
 return lhs.base() - rhs.base();

・enable_if / is_integral

vectorのconstructer(5)を読む
・実装サンプル
https://secret-garden.hatenablog.com/entry/2016/12/22/032008

・vector constructer(5)

 template <typename InputIterator>
 vector(InputIterator first, InputIterator last,
        const Allocator& allocator = Allocator(),
        typename std::enable_if<!std::is_integral<InputIterator>::value,
                                InputIterator>::type* = NULL)
1引数 InputIterator first
第2引数 InputIterator last
第3引数 const Allocator& allocator = Allocator()
第4引数 typename std::enable_if<!std::is_integral<InputIterator>::value,
                                InputIterator>::type* = NULL

・第4引数について。
・もし InputIterator == int だった場合、

std::enable_if<!std::is_integral<InputIterator>::value, InputIterator>::type* = NULL
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ == true
               ~ == false
std::enable_if<false, >になるので、::type* が定義されず、第4引数が存在しない扱いになる。
よって、オーバーロードが適応されない。


・もし InputIterator == iterator だった場合、

std::enable_if<!std::is_integral<InputIterator>::value, InputIterator>::type* = NULL
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ == false
               ~ == true
std::enable_if<true, >になるので、::type* が定義され、第4引数が存在する扱いになる。
よって、オーバーロードが適応される。

・テンプレートってなに?ってところからSFINAEあたりまで頑張る
https://dora119.hateblo.jp/entry/2017/12/05/001607

std::is_initegralというのはTが整数の時に::valueがtrue、そうでなければfalseになるテンプレートです
大体予想できているかもしれませんが、intCheck(10)のように呼び出すと、引数10は整数なのでenable_if::typeが存在し、"xは整数"と出力されます
一方intCheck(1.5)のようにするとenable_if::typeは存在せずエラーになるというわけです
こんな風にしてテンプレート版if文ができちゃいました、SFINAE強い

「::type」の有無でオーバーロード判定を分ける機構のことをSFINAEという。すふぃ姉。

実装します。

/utils/util.hpp

#ifndef UTIL_HPP
#define UTIL_HPP

namespace ft {

/// integral_constant

template <typename _Tp, _Tp __v>
struct integral_constant {
 static const _Tp value = __v;
 typedef _Tp value_type;
 typedef integral_constant<_Tp, __v> type;
 const value_type operator()() const { return value; }
};

/// The type used as a compile-time boolean with true value.
typedef integral_constant<bool, true> true_type;

/// The type used as a compile-time boolean with false value.
typedef integral_constant<bool, false> false_type;


};  // namespace ft
#endif

Q. integral_constant
A. is_integral / enable_if のメタ構造体。
覚えておくのは2つの特徴だけで、
1. valueとvalue_typeの定義があること。
2. 
true_type::value をとったときに true。
false_type::value をとったときに falseを返す構造体。
とでも思っておきます。

本家にある

constexpr operator value_type() const { return value; } // c++11

戻り値のないoperatorとは?という感じですが、c++11の機能「conversion function: 変換関数」で、割愛します。ともなって、テストケース

 ft::is_integral<int>() // 1
 ft::is_integral<int*>() // 0

これらの出力は含めません。

Q.  const value_type operator()() const { return value; }
A. こう呼び出せます。

 ft::is_integral<int> F;
 int f = F(); // f == 1

/utils/util.hpp

/// is_same
/*
通常はfalse_typeを継承
型が同じ場合だけtrueになる
*/
template <typename, typename>
struct is_same : public false_type {};

template <typename _Tp>
struct is_same<_Tp, _Tp> : public true_type {};

Q. is_same()
A.
is_same<int, int>::valueでtrue
is_same<int, char>::valueでfalse がとれます。使いません。

/utils/util.hpp

/// remove_const
template <typename T>
struct remove_const {
 typedef T type;
};

template <typename T>
struct remove_const<T const> {
 typedef T type;
};

/// remove_volatile
template <typename T>
struct remove_volatile {
 typedef T type;
};

template <typename T>
struct remove_volatile<T volatile> {
 typedef T type;
};

/// remove_cv
template <typename T>
struct remove_cv {
 typedef typename remove_const<typename remove_volatile<T>::type>::type type;
};

/// helper

template <typename>
struct __is_integral_helper : public false_type {};

template <>
struct __is_integral_helper<bool> : public true_type {};

template <>
struct __is_integral_helper<char> : public true_type {};

template <>
struct __is_integral_helper<signed char> : public true_type {};

template <>
struct __is_integral_helper<unsigned char> : public true_type {};

template <>
struct __is_integral_helper<wchar_t> : public true_type {};

//template <>
//struct __is_integral_helper<char16_t> : public true_type {};

//template <>
//struct __is_integral_helper<char32_t> : public true_type {};

template <>
struct __is_integral_helper<short> : public true_type {};

template <>
struct __is_integral_helper<unsigned short> : public true_type {};

template <>
struct __is_integral_helper<int> : public true_type {};

template <>
struct __is_integral_helper<unsigned int> : public true_type {};

template <>
struct __is_integral_helper<long> : public true_type {};

template <>
struct __is_integral_helper<unsigned long> : public true_type {};

template <>
struct __is_integral_helper<long long> : public true_type {};

template <>
struct __is_integral_helper<unsigned long long> : public true_type {};

//// is_integral
template <typename T>
struct is_integral
   : public __is_integral_helper<typename remove_cv<T>::type>::type {};

Q. is_integral()
A. 最後のis_integralを読み解きます。
1. remove_cv は、const / volatile修飾を外した型Tを返します。
2. __is_integral_helper<> は、整数型なら true_type、そうじゃないならfalse_typeを返すように定義しています。
まず、基本的にfalse_typeを返すように定義。

template <typename>
struct __is_integral_helper : public false_type {};

そのあと、すべての整数型でtrue_typeを定義しています。
以上から、
is_integral<int>::value == true だし、
is_integral<iter>::value == false が得られます。

最後に、

/utils/util.hpp

template <bool B, class T = void>
struct enable_if {};

template <class T>
struct enable_if<true, T> {
 typedef T type;

enable_if<true, T>::type が存在し、
enable_if<false, T>は、typedef type がないので、「::type」が得られません。ここがオーバーロード判定に利用されています。

Q. 継承が嫌い。 : public true_type() をしない場合

value と value_type の定義が必須です。

template <>
struct __is_integral_helper<int>{
 static const bool value = true;
 typedef int value_type;
 typedef __is_integral_helper<int> type;
 const value_type operator()() const { return value; }
};

とでも書きます。これを全部書くの大変。​

オーバーロードの判定に::typeの有無を使う是非はわかりませんが、スマートなのかわからないけど、なるほど解決という気分になります。

・equal/lexicographical compare

https://cpprefjp.github.io/reference/algorithm/equal.html
https://cpprefjp.github.io/reference/algorithm/lexicographical_compare.html
サンプルコードが明解です。

#ifndef ALGORITHM_HPP
#define ALGORITHM_HPP

namespace ft {
template <class InputIt1, class InputIt2>
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) {
 for (; first1 != last1; ++first1, ++first2)
   if (!bool(*first1 == *first2)) return false;
 return true;
}

template <class InputIt1, class InputIt2, class BinaryPredicate>
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2,
          BinaryPredicate p) {
 for (; first1 != last1; ++first1, ++first2)
   if (!bool(p(*first1, *first2))) return false;
 return true;
}

template <class InputIt1, class InputIt2>
bool lexicographical_compare(InputIt1 first1, InputIt1 last1, InputIt2 first2,
                            InputIt2 last2) {
 for (; (first1 != last1) && (first2 != last2); ++first1, (void)++first2) {
   if (*first1 < *first2) return true;
   if (*first2 < *first1) return false;
 }
 return (first1 == last1) && (first2 != last2);
}

template <class InputIt1, class InputIt2, class Compare>
bool lexicographical_compare(InputIt1 first1, InputIt1 last1, InputIt2 first2,
                            InputIt2 last2, Compare comp) {
 for (; (first1 != last1) && (first2 != last2); ++first1, (void)++first2) {
   if (comp(*first1, *first2)) return true;
   if (comp(*first2, *first1)) return false;
 }
 return (first1 == last1) && (first2 != last2);
}
};  // namespace ft
#endif

これでft::vectorを実装するアイテムが整いました。
vectorチュートリアルだけだと、実は結構な関数が欠けています。埋めていきます。

[(3/6)vector実装にすすむ] 

https://note.com/syamashi/n/n329e33f09bb3


この記事が気に入ったらサポートをしてみませんか?