Как это делается
В этом примере мы реализуем два разных пользовательских строковых класса: lc_string
ci_string
. Первый создает на основе любых входных данных строки в нижнем регистре. Второй строки не преобразует, но может выполнить сравнение строк независимо от регистра.1. Включим несколько необходимых заголовочных файлов, а затем объявим об использовании пространства имен std
#include
#include
#include
using namespace std;
2. Затем снова реализуем функцию std::tolower
. Уже существующая функция работает хорошо, но она не имеет модификатора 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
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
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; }
}