Что такое traits?
Страница 3.


Дело в том, что компилятор не позволяет взять адрес 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

 

 
« Предыдущая статья   Следующая статья »