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


Тогда объекты нашего динамического массива будем создавать так:

vector<std::string> vec1; // эквивалентно: vector<std::string, elem_traits<std::string> >
// для всех типов, для которых не сделана специализация,
// все остается как прежде:
// тогда:
// arg_type  = const std::string& (по ссылке)
// reference = std::string&
// const_reference = const std::string&


// а для типа char мы сделали специализацию
vector<char> vec2; // эквивалентно: vector<char, elem_traits<char> >
// тогда:
// arg_type  = const char (по значению, а не по ссылке)
// reference = char&
// const_reference = const char&

Давайте теперь рассмотрим случай, когда мы хотим создать динамический массив с элементами типа char, но чтобы arg_type был эквивалентен char&. Специализация нашего класса elem_traits для char уже существует, то есть ее сделать мы уже не можем. В таком случае остается создать новый класс свойств:
// обратите внимание: структура, а не класс
// (разницы никакой, это лишний раз подчеркивается данным примером)
struct char_elem_traits {
 // здесь переопределеяем тип аргумента функций.
 // Мы договорились, что это будет char&
    typedef       char& arg_type; // определили тип, который передает по ссылке типы char

    // остальное оставляем так же.
    // Хотя ничто не мешает нам переопределеить тип ссылки или константной ссылки
    typedef       char& reference;
    typedef const char& const_reference;
};

Тогда осталось только создать нужный динамический массив:
vector<char, char_elem_traits> vec;
// тогда:
// arg_type  = char& (по ссылке, но не по константной)
// reference = char&
// const_reference = const char&

Но заметим, что класс(структура) char_elem_traits переопределяет только один тип - arg_type, а остальные остаются неимзенными по отношению к elem_traits<char>. То есть мы произвели лишнюю работу, определив самостоятельно типы reference и const_reference. Хорошо еще, что тут немного типов, а представьте, что их было бы около 20? 50? Чтобы каждый раз не переписывать общие свойства, можно воспользоваться открытым наследованием и переопределить нужные нам типы:
class char_elem_traits : public elem_traits<char> {
 public:
  typedef char& arg_type; // определили тип, который передает по ссылке типы char

  // типы reference и const_reference наследуются
};

Теперь можно использовать наш класс свойств char_elem_traits точно так же, как мы делали это раньше.

До этого мы рассматривали только свойства, определяющие необходимые типы. Еще могут быть свойства-значения: они предоставляют нужные константы для данного типа.
Рассмотрим пример: нам надо написать шаблон класса, которому для каждого параметра шаблона(типа) требуются связанные с ним константы. Первая мысль будет такой(пример немного перефразирован из вопроса с форума):
template <typename T,
          std::size_t T2 = sizeof(T) * 4,
          std::size_t T3 = sizeof(T) * 2,
          std::size_t T4 = sizeof(T)
          // ...
          >
class X {
  // используем нужные константы
  // T2 == sizeof(T) * 4
  // T4 == sizeof(T)
};

Но мы теперь люди продвинутые и знаем, как избежать такого большого количества аргументов шаблона - обернуть все в traits:
template <typename T>
struct x_traits {
 static std::size_t T2 = sizeof(T) * 4;
 static std::size_t T3 = sizeof(T) * 2;
 static std::size_t T4 = sizeof(T);
};

template <typename T, typename traits = x_traits<T> >
class X {
  // используем нужные константы через traits:
  // traits::T2 == sizeof(T) * 4
  // traits::T4 == sizeof(T)
};

Но теперь в нашем коде возникает проблема: если вдруг получится так, что пользователь нашего класса захочет взять адрес нашей константы, компилятор должен будет создать реальную константу в памяти, адрес которой можно взять. Для этого был предуман трюк с enum'ом:
template <typename T>
struct x_traits {
 enum { T2 = sizeof(T) * 4 };
 enum { T3 = sizeof(T) * 2 };
 enum { T4 = sizeof(T)      };
};

template <typename T, typename traits = x_traits<T> >
class X {
  // используем нужные константы через traits:
  // traits::T2 == sizeof(T) * 4
  // traits::T4 == sizeof(T)
};

 

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