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
ci_string
расшифровывается как std::string
своими классами-типажами для символов:using lc_string = basic_string
using ci_string = basic_string
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: "
<< ci_string{"Foo Bar Baz"} << '\n';
14. Чтобы протестировать строку, не зависящую от регистра, можно создать две строки, которые, по сути, равны, но регистры отдельных символов различаются. При выполнении сравнения, не зависящего от регистра, они должны показаться равными:
ci_string user_input {"MaGiC PaSsWoRd!"};
ci_string password {"magic password!"};
15. Сравним их и выведем на экран сообщение об их совпадении, если это так:
if (user_input == password) {
cout << "Passwords match: \"" << user_input
<< "\" == \"" << password << "\"\n";
}
}
16. Компиляция и запуск программы дадут ожидаемый результат. При выводе на экран одинаковых строк с разными типами мы получили одинаковые результаты, но в строке типа lc_string
$ ./custom_string
string: Foo Bar Baz
lc_string: foo bar baz
ci_string: Foo Bar Baz
Passwords match: "MaGiC PaSsWoRd!" == "magic password!"
Как это работает
Вся работа по созданию подклассов и повторной реализации функций, конечно же, для новичков выглядит несколько странно. Откуда появились все сигнатуры функций, из которых мы
Сначала взглянем, откуда появился класс std::string
template <
class CharT,
class Traits = std::char_traits
class Allocator = std::allocator
>
class basic_string;