C++ 中的特征是什么,尤其是 boost 中的特征

Eme*_* Xu 2 c++ boost traits

我正在研究 Boost 库,发现它大量使用了特征概念,例如 iterator_traits、graph_traits。特质是什么意思?您能否给我一个简单而简洁的例子来告诉我们为什么需要特质。据我目前所知,“特征”似乎意味着它包含我们可能需要的所有类型,这样我们就不会在类型上出错。
以下是boost中的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;
  };
Run Code Online (Sandbox Code Playgroud)

Nic*_*ico 7

我将通过一个简单的示例来解释我如何看待特征类。

定义可以是:特征允许以非侵入性方式扩展 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);
};
Run Code Online (Sandbox Code Playgroud)

您选择模板化 Point2D 类,以便您的库的用户可以选择适当的类型(如果需要精度则为 double,如果需要处理像素则为 int,...)。例如,在 Qt 中,它们将 int 强加为坐标类型,这可能会阻碍您的项目。类型 T 需要提供一些有关坐标类型概念的信息:在 Point2D 类中,您需要对 T 进行处理:

  • 获取标量类型 T 可以乘以(在我的运算符 *= 中,我写了 double 但它可能太干扰)
  • 获取 T 的字符串表示形式
  • 比较 2 T (实现您的运算符==)...

如果您编写自己的坐标类,则可以提供所有内容。但是如果你的库的用户想要使用 int 作为 T,他就不能扩展 int。

这里到达了特征:您的 Point2D 将使用特征类 CoordTypeTraits。它的目的是“扩展” T 类型以提供 T 作为坐标概念(函数、typedef...)所需的一切

样本:

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);
}
Run Code Online (Sandbox Code Playgroud)

现在,借助特征类 CoordTypeTraits,您可以在代码中访问有关 T 所需的信息:

/*
 * @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();
    }
};
Run Code Online (Sandbox Code Playgroud)

你的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);
    } 
}
Run Code Online (Sandbox Code Playgroud)

以 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);
    } 
}
Run Code Online (Sandbox Code Playgroud)

结论和备注

您的库提供了一个 Point2D 类,因此用户可以使用您提供的所有功能(比较、平移、旋转等)。如果他提供一个特征来将其类型作为坐标来处理,他就可以对任何他想要的坐标类型执行此操作。通常,库提供一些共同的特征(这里我们将提供 Point_double 和 Point_int)。

备注:我没有尝试编译,代码只是为了说明。