C++ 中的特征是什么,尤其是在 boost 中
What are traits in C++, especially in boost
我正在研究 Boost 库,发现它经常使用 traits 概念,例如 iterator_traits、graph_traits。
特质是什么意思?你能给我一个简单而简洁的例子来告诉我们为什么我们需要特质吗?
据我目前所知,"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;
};
可以用 "characteristics".
替换(作为一个词)特征
它们是一种 C++ 技术,使用模板专门化将不同的类型、操作和常量关联到一个类型(有效地创建元数据)。
我将通过一个简单的示例来解释我如何看待特征 class。
定义 可以是:特征允许以非侵入方式扩展 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 class 以便您的库的用户可以选择适当的类型(如果需要精度则为 double,如果他使用像素则为 int,...)。例如,在 Qt 中,它们将 int 强加为坐标类型,这可能会阻碍您的项目。
类型 T 需要提供有关坐标类型概念的一些信息:在 Point2D class 中,您需要使用 T:
- 获取可与 T 相乘的标量类型(在我的运算符 *= 中,我写了 double 但它可能太麻烦了)
- 获取 T 的字符串表示
- 比较 2 T(实现你的运算符==)...
如果你自己写坐标class,你可以提供所有的东西。但是如果你的库的用户想要使用 int 作为 T,他就不能扩展 int。
特征来了:您的 Point2D 将使用特征 class CoordTypeTraits。它的目的是"extend" 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);
}
现在您可以在代码中访问您需要的有关 T 的信息,这要归功于特征 class 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)"add" 坐标概念数据到 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 class,因此用户可以使用您提供的所有功能(比较、翻译、旋转...)。
如果他提供一个特性来将其类型作为坐标来处理,他可以使用他想要的任何坐标类型来做到这一点。
通常,库会提供一些共同特征(这里我们会提供 Point_double 和 Point_int)。
备注:我没有尝试编译,代码只是为了说明。
我正在研究 Boost 库,发现它经常使用 traits 概念,例如 iterator_traits、graph_traits。
特质是什么意思?你能给我一个简单而简洁的例子来告诉我们为什么我们需要特质吗?
据我目前所知,"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;
};
可以用 "characteristics".
替换(作为一个词)特征它们是一种 C++ 技术,使用模板专门化将不同的类型、操作和常量关联到一个类型(有效地创建元数据)。
我将通过一个简单的示例来解释我如何看待特征 class。
定义 可以是:特征允许以非侵入方式扩展 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 class 以便您的库的用户可以选择适当的类型(如果需要精度则为 double,如果他使用像素则为 int,...)。例如,在 Qt 中,它们将 int 强加为坐标类型,这可能会阻碍您的项目。 类型 T 需要提供有关坐标类型概念的一些信息:在 Point2D class 中,您需要使用 T:
- 获取可与 T 相乘的标量类型(在我的运算符 *= 中,我写了 double 但它可能太麻烦了)
- 获取 T 的字符串表示
- 比较 2 T(实现你的运算符==)...
如果你自己写坐标class,你可以提供所有的东西。但是如果你的库的用户想要使用 int 作为 T,他就不能扩展 int。
特征来了:您的 Point2D 将使用特征 class CoordTypeTraits。它的目的是"extend" 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);
}
现在您可以在代码中访问您需要的有关 T 的信息,这要归功于特征 class 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)"add" 坐标概念数据到 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 class,因此用户可以使用您提供的所有功能(比较、翻译、旋转...)。 如果他提供一个特性来将其类型作为坐标来处理,他可以使用他想要的任何坐标类型来做到这一点。 通常,库会提供一些共同特征(这里我们会提供 Point_double 和 Point_int)。
备注:我没有尝试编译,代码只是为了说明。