Что такое трейты в C ++, особенно в boost

Я изучаю библиотеку Boost и обнаружил, что она много использует концепцию трейтов, например, iterator_traits, graph_traits. Что означает черта? Не могли бы вы привести мне простой, но краткий пример, который объяснит нам, зачем нам нужны черты характера. Насколько мне известно, термин "черты" означает, что он содержит все типы, которые могут нам понадобиться, так что мы не ошибемся с типами.
Ниже приведен шаблон graph_traits в усилении:

template <typename Graph>
  struct graph_traits {
    typedef typename Graph::vertex_descriptor      vertex_descriptor;
    typedef typename Graph::edge_descriptor        edge_descriptor;
    typedef typename Graph::adjacency_iterator     adjacency_iterator;
    typedef typename Graph::out_edge_iterator      out_edge_iterator;
    typedef typename Graph::in_edge_iterator       in_edge_iterator;
    typedef typename Graph::vertex_iterator        vertex_iterator;
    typedef typename Graph::edge_iterator          edge_iterator;

    typedef typename Graph::directed_category      directed_category;
    typedef typename Graph::edge_parallel_category edge_parallel_category;
    typedef typename Graph::traversal_category     traversal_category;

    typedef typename Graph::vertices_size_type     vertices_size_type;
    typedef typename Graph::edges_size_type        edges_size_type;
    typedef typename Graph::degree_size_type       degree_size_type;
  };

person Emerson Xu    schedule 27.05.2015    source источник
comment
Как работают классы признаков?   -  person Piotr Skotnicki    schedule 27.05.2015


Ответы (2)


Я объясню, как я вижу класс черт на простом примере.

Определение может быть таким: характеристика позволяет ненавязчиво расширять T.

Образец

Представьте, что вы хотите предоставить геометрическую библиотеку, содержащую концепцию 2D-точки (координаты X, Y). Вы предоставляете

/*
 * @tparam T the coordinate type
 */
template <typename T>
class Point2D
{
    T _x;
    T _Y;
public:
    /* some ctors */

    /*
     * Multiply this point by the given factor. 
     * @post this._x = factor * this._x
     *       this._y = factor * this._y
     * @return reference to *this.
     */
    Point2D<T> operator*=(double factor);
};

Вы выбираете шаблон класса Point2D, чтобы пользователь вашей библиотеки мог выбрать соответствующий тип (double, если требуется точность, int, если он работает с пикселями, ...). В Qt, например, они вводят int в качестве типа координат, что может блокировать ваш проект. Тип T должен предоставить некоторую информацию о концепции типа координат: в вашем классе Point2D вам нужно будет работать с T:

  • Получить скалярный тип T можно умножить на (в моем операторе * = я написал double, но это может быть слишком навязчивым)
  • Получить строковое представление T
  • Сравните 2 T (чтобы реализовать оператор ==) ...

Если вы напишете свой собственный класс Coordinate, вы сможете предоставить все необходимое. Но если пользователь вашей библиотеки хочет использовать int как T, он не может расширить int.

Вот и черты: ваш Point2D будет использовать класс черт CoordTypeTraits. Его цель - «расширить» тип T, чтобы предоставить все, что вам нужно, от T как концепции координат (функция, определение типа ...)

Образец:

typename <typedef T>
struct CoordTypeTraits
{
    /*
     * Define the scalar type T is multipiable with
     */ 
    // typedef ... ScalarType;

    /*
     * Check the equality between 2 T, with a given precision
     */
    // static bool IsCloseTo(const T& coord1, const T& coord2);

    /*
     * Return a string representation of T.
     */
     // static string ToString(const T& coord);
}

Теперь вы можете получить доступ к необходимой информации о T в своем коде благодаря классу свойств CoordTypeTraits:

/*
 * @tparam T the coordinate type
 */
template <typename T>
class Point2D
{
    T _x;
    T _Y;
public:
    /* some ctors */

    /*
     * @post this._x = factor * this._x
     *       this._y = factor * this._y
     * @return reference to *this.
     */
    Point2D<T> operator*=(CoordTypeTraits<T> factor);

    const bool operator==(const T& rhs)
    {
        return CoordTypeTraits<T>.IsCloseTo(*this, rhs);
    }

    string ToString() const
    {
        return CoordTypeTraits<T>.ToString();
    }
};

Пользователь вашей библиотеки lib будет использовать ваш Point2D с типом, подходящим для него, и он должен предоставить (путем специализации CoordTypeTraits для этого типа) черты, чтобы «добавить» данные концепции координат в T.

Например, с двойным:

typedef Point2D<double> Point_double;

// specialization of CoordTypeTraits for double coordinate type
template<>
struct CoordTypeTraits<double>
{
    typedef double ScalarType;

    static bool IsCloseTo(const T& coord1, const T& coord2)
    {
        return (coord1 - coord2 < GetPrecision()) && (coord2 - coord1 < GetPrecision());
    }

    private:
    static const double GetPrecision()
    {
        return 0.001; // because double is micrometers by convention, and I need nanometer precision
    }

    static string ToString(const double& coord)
    {
        return boost::lexical_cast<string>(coord);
    } 
}

Например, с int:

typedef Point2D<int> Point_int;

// specialization of CoordTypeTraits for double coordinate type
template<>
struct CoordTypeTraits<int>
{
    typedef int ScalarType;

    static bool IsCloseTo(const T& coord1, const T& coord2)
    {
        return coord1 == coord2;
    }

    static string ToString(const int& coord)
    {
        return boost::lexical_cast<string>(coord);
    } 
}

Заключение и примечания

Ваша библиотека предоставляет класс Point2D, поэтому пользователь может использовать все предоставляемые вами функции (сравнивать, переводить, вращать и т. Д.). Он может сделать это с любым типом координат, который пожелает, если он предоставит свойства для обработки этого типа в качестве координаты. Обычно библиотеки предоставляют некоторые общие черты (здесь мы предоставляем Point_double и Point_int).

Реплика: я не пытался компилировать, код просто для иллюстрации.

person Nico    schedule 27.05.2015

Черты можно было заменить (как слово) на «характеристики».

Это метод C ++, который использует специализацию шаблонов для связывания различных типов, операций и констант с типом (фактически создавая метаданные).

person utnapistim    schedule 27.05.2015