chitay-knigi.com » Разная литература » C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 71 72 73 74 75 76 77 78 79 ... 121
Перейти на страницу:
от регистра.

1. Включим несколько необходимых заголовочных файлов, а затем объявим об использовании пространства имен std по умолчанию:

#include <iostream>

#include <algorithm>

#include <string>

using namespace std;

2. Затем снова реализуем функцию std::tolower, которая уже определена в <cctype>. Уже существующая функция работает хорошо, но она не имеет модификатора constexpr. Однако отдельные строковые функции имеют этот модификатор, начиная с C++17, и мы хотим иметь возможность использовать их для нашего собственного строкового класса-типажа. Функция соотносит символы в верхнем регистре с символами в нижнем регистре и оставляет другие символы неизменными:

static constexpr char tolow(char c) {

  switch (c) {

    case 'A'...'Z': return c - 'A' + 'a';

    default: return c;

  }

}

3. Класс std::basic_string принимает три шаблонных параметра: тип основного символа, класс-типаж символа и тип allocator. В этом разделе мы изменяем только класс-типаж символа, поскольку он определяет поведение строк. Для повторной реализации только того, что должно отличаться от типичного поведения строк, мы явно наследуем от стандартного класса-типажа:

class lc_traits : public char_traits<char> {

public:

4. Наш класс принимает входные строки, но преобразует их в строки в нижнем регистре. Существует функция, которая делает это посимвольно, так что можно поместить здесь нашу функцию tolow. Она имеет модификатор constexpr, именно поэтому мы и реализовали самостоятельно функцию tolow с таким же модификатором constexpr:

  static constexpr

  void assign(char_type& r, const char_type& a ) {

    r = tolow(a);

  }

5. Еще одна функция обрабатывает копирование целой строки в отдельный участок памяти. Мы используем вызов std::transform, чтобы скопировать все символы из исходной строки в строку — место назначения, и в то же время соотносим каждый символ с его версией в нижнем регистре:

  static char_type* copy(char_type* dest,

                         const char_type* src,

                         size_t count) {

    transform(src, src + count, dest, tolow);

    return dest;

  }

};

6. Еще один типаж помогает создать строковый класс, эффективно преобразующий строки в нижний регистр. Напишем еще один типаж, который не изменяет полученную строку, но позволяет выполнять сравнение строк независимо от регистра. Снова унаследуем от существующего стандартного класса-типажа для символов и в этот раз переопределим некоторые члены функции:

class ci_traits : public char_traits<char> {

public:

7. Функция eq указывает, равны ли два символа. Реализуем такую же функцию, но будем сравнивать версии символов в нижнем регистре. При использовании этой функции символ 'A' равен символу 'a'.

  static constexpr bool eq(char_type a, char_type b) {

    return tolow(a) == tolow(b);

}

8. Функция lt указывает, меньше ли значение a значения b. Применим корректный логический оператор, но только после того, как оба символа будут преобразованы к нижнему регистру:

  static constexpr bool lt(char_type a, char_type b) {

    return tolow(a) < tolow(b);

}

9. Последние две функции работали для посимвольного ввода, а следующие две будут работать для строк. Функция compare работает по аналогии со старой функцией strncmp. Она возвращает значение 0 при условии, что обе строки равны от начала до позиции, указанной в переменной count. Если они отличаются, то функция возвращает отрицательное или положительное число, которое указывает, какая из строк была меньше с точки зрения лексикографии. Определение разности между обоими символами в каждой позиции должно, конечно же, происходить для символов в нижнем регистре. Положительный момент заключается в том, что весь код цикла является частью функции с модификатором constexpr, начиная с C++14.

  static constexpr int compare(const char_type* s1,

                               const char_type* s2,

                               size_t count) {

  for (; count; ++s1, ++s2, --count) {

    const char_type diff (tolow(*s1) - tolow(*s2));

    if (diff < 0) { return -1; }

    else if (diff > 0) { return +1; }

  }

  return 0;

}

10. Последняя функция, которую нужно реализовать для нашего строкового класса, не зависящего от регистра, — это функция find. Для заданной входной строки p и ее длины count она определяет позицию символа ch. Затем возвращает указатель на первое включение данного символа или nullptr, если такого символа нет. Сравнение в этой функции должно выполняться с использованием функции tolow, чтобы поиск не зависел от регистра. К сожалению, мы не можем применить функцию std::find_if, поскольку она не имеет модификатора constexpr, нужно писать цикл самостоятельно.

  static constexpr

  const char_type* find(const char_type* p,

                        size_t count,

                        const char_type& ch) {

    const char_type find_c {tolow(ch)};

    for (; count != 0; --count, ++p) {

      if (find_c == tolow(*p)) { return p; }

    }

    return nullptr;

  }

};

11. О’кей, с типажами мы закончили. Теперь можно определить два новых строковых типа. lc_string означает lower-case string (строка в нижнем регистре). ci_string расшифровывается как case-insensitive string (строка, не зависящая от регистра). Оба класса отличаются от класса std::string своими классами-типажами для символов:

using lc_string = basic_string<char, lc_traits>;

using ci_string = basic_string<char, ci_traits>;

12. Чтобы позволить потокам вывода принимать эти новые классы для вывода на экран, нужно перегрузить потоковый оператор <<:

ostream& operator<<(ostream& os, const lc_string& str) {

  return os.write(str.data(), str.size());

}

ostream& operator<<(ostream& os, const ci_string& str) {

  return os.write(str.data(), str.size());

}

13. Теперь наконец можно начать реализовывать саму программу. Создадим экземпляр обычной строки, строки в нижнем регистре и строки, не зависящей от регистра, и сразу же выведем их на экран. Строки в нижнем регистре будут соответствовать своим названиям — их символы будут иметь нижний регистр:

int main()

{

  cout << " string: "

       << string{"Foo Bar Baz"} << 'n'

       << "lc_string: "

       << lc_string{"Foo Bar Baz"} << 'n'

       << "ci_string: "

1 ... 71 72 73 74 75 76 77 78 79 ... 121
Перейти на страницу:

Комментарии
Минимальная длина комментария - 25 символов.
Комментариев еще нет. Будьте первым.