Здравствуйте, Аноним, Вы писали:

А>И в чем их смысл? В частности char_traits, например.


В расширении, группировке и обобщении инструментария, относящегося к тому или иному типу.

Допустим, у нас есть некий класс T, и нам потребовалось добавить функциональность — приведение к целому и обратно.
Казалось бы, нет проблем: вводим в его определение три имени:
class T
{
  ...
public: // convert to/from int
  typedef ??? integral_type; // потому что может статься, что int не всегда нас устраивает
  integral_type to_integral() const;
  static T from_integral(integral_type); // либо конструктор; но производящая функция - это более общее решение
};

Чем это плохо?
1) Если таких классов много, то придётся в каждый из них включить подобную функциональность. А не факт, что можно модифицировать их определения
2) Для простых типов — определения модифицировать нельзя.

Ну ладно, нельзя так нельзя — специально для этого вводятся операции НАД типом, т.е. перегруженные внешние функции.
someint to_integral(const T& src); // тип результата - по желанию.
// Жаль, что пропал идентификатор типа, специфичного для T, ну да фиг с ним
template<class _> void from_integral(someint){} // первичное объявление шаблона
template<> T from_integral<T>(someint src); // и его специализация для конкретного типа

Чем плохи шаблоны функций со свободными (не выводимыми из аргументов) параметрами шаблона — уже говорилось недавно.
Начиная с неоднозначности и нетривиальных приоритетов при выборе той или иной специализации.
И ещё остаются проблемы, связанные с автоматическим приведением типов аргументов. Полагаясь на выведение типа выражения, можно приплыть в неожиданном направлении.

Кроме того, мы получаем россыпь. Ладно конверсия, это всего лишь пара функций. А та же работа с символами, где функций больше десятка?

Кроме того, покуда нет шаблонных typedef'ов и шаблонных же переменных, мы не можем вводить их иным способом, как завести некий вспомогательный класс по шаблону, параметризованному данным типом.
Он и будет являться traits'ом.
// первичное объявление шаблона
template<class XZ> struct integral_traits
{
  typedef int inegral_type; // по умолчанию, int
  static integral_type to(const XZ& src) { return to_integral(src); }
    // по умолчанию, надеемся, что есть глобальная функция
  static XZ from(integral_type src) { XZ dst; from_integral(src,dst); return dst }
    // по умолчанию, надеемся, что есть глобальная функция с out-параметром
};

template<> struct integral_traits<T> // для нашего любимого, со встроенной функциональностью
{
  typedef T::integral_type integral_type;
  static integral_type to(const T& src) { return src.to_integral(); }
  static T from(integral_type src) { return T::from_integral(src); }
}

Наконец, мы можем действовать довольно изощрённо: если классы T, U, V являются моделью "ВстроенныйКонвертор", то можем обобщить эту специализацию
template<class XZ>
struct integral_traits_embedded
{
  typedef XZ::integral_type integral_type;
  static integral_type to(const XZ& src) { return src.to_integral(); }
  static XZ from(integral_type src) { return XZ::from_integral(src); }
};

template<class XZ>
struct integral_traits_outer
{
  typedef int inegral_type; // по умолчанию, int
  static integral_type to(const XZ& src) { return to_integral(src); }
  static XZ from(integral_type src) { XZ dst; from_integral(src,dst); return dst }
};

template<class XZ>
struct is_embedded_integral_convertor
{
  enum { value = ????? }; // для не-конверторов - false
};
// определение этого свойства (ещё один traits!) может быть автоматизированным
// или же у каждого класса-конвертора должна быть своя специализация

template<class XZ>
struct integral_traits : typename boost::mpl::if_c<
                               is_embedded_integral_convertor<XZ>::value,
                               integral_traits_embedded<XZ>,
                               integral_traits_outer<XZ>
                             >::type
{
};

Здесь мы увидели ещё одно применение traits'ов — а именно, паттерн "Стратегия". Определение (при компиляции) и поведение (при исполнении) окончательной версии integral_traits зависит от того, какая стратегия — integral_traits_embedded | integral_traits_outer — будет выбрана, мы станем пользоваться тем или иным комплектом функций.
Автор: Кодт    Оценить