Что такое traits? Страница 3.
|
Страница 3 из 4
Дело в том, что компилятор не позволяет взять адрес enum-констант. Так что мы теперь точно знаем, что наша константа будет вставлена в код числом. На данном этапе все хорошо. Но вдруг нам надо доработать класс так, что нужна константа вещественного типа. Но возникает такая проблема: в классах можно инициализировать только статические интегральные константы. В enum'ах тоже можно использовать только целые типы. Что же тогда делать? Остается только определить inline-фунцию, которая бы возвращала нужное значение: template <typename T> struct x_traits { static std::size_t T2() { return(sizeof(T) * 4); }
static std::size_t T3() { return(sizeof(T) * 2); }
static std::size_t T4() { return(sizeof(T)); }
// возвращаемое значение типа double static double T5() { return(sizeof(T) * 5 / 7); } };
template <typename T, typename traits = x_traits<T> > class X { // используем нужные константы через traits: // T2: traits::T2() - возвращает тип std::size_t, можно и через enum // T5: traits::T5() - возвращает тип double, по-другому не сделать, только функция };
| Надо заметить, что наличие функции на производительность не влияет, так как современные компиляторы способны подставить нужное значение прямо в код вместо вызова функции. Также можно сочетать наличие функций, возвращающих нужные значения вещественного типа, и простые интегральные константы, полученные с помощью enum.
Теперь мы знаем основные принципы для работы с traits. Так что давайте рассмотрим пример, который помогает расширить стандартную библиотеку C++. Давайте рассмотрим такую задачу(перефразировано из вопроса с форума): Цитата | Я хочу определить размер файла с помощь класса ifstream. Сам файл весит больше 5 Гб. Функция tellg() возвращает какое-то нереальное значение. Как можно правильно определить размер файла? | На данный момент все известные мне версии стандартной библиотеки C++ представляют позицию в файле 32-разрядным целым. Но дело в том, что обычные 32-разрядные целые числа не могут представлять размер файла, большего 4 ГБ(происходит переполнение). То есть нам надо каким-либо образом заставить стандартную библиотеку использовать не 32-разрядные числа, а, например 64-разрядные(или вообще наш собственный тип(класс), который мы опишем). Как это сделать? Как вы уже догадались, помогут нам traits. Как известно, ifstream - это только typedef от класса basic_ifstream. Сам же класс basic_ifstream принимает 2 параметра шаблона: первый из них определяет тип символа, а второй определяет свойста(traits). Так вот эти свойста и должны определять, каким типом представлять позицию в файле, как сравнивать символы и тд. Второй параметр шаблона класса basic_ifstream по умолчанию будет классом char_traits. Это стандартный класс, который описывает основные свойста: нужные типы, как сравнивать символы, присваивать и тд.. Так как мы не собираемся переопределять это все(нам надо заменить только 2 типа), тогда хорошей идеей будет унаследоваться от класса char_traits. У класса char_traits есть 2 интересующих нас типа(полный список типов можно найти в документации): 1) pos_type - тип, используемый для представления позиции в потоке 2) off_type - тип, используемый для представления смещений между позициями в потоке Вот их-то как раз нам и надо переопределить. Давайте сделаем первую попытку: template <typename char_t> struct long_pointer_traits : public std::char_traits<char_t> { typedef __int64 pos_type; typedef __int64 off_type; };
typedef std::basic_ifstream<char, long_pointer_traits<char> > long_ifstream;
// используем long_ifstream
| |