特征中的 Typedef 与 class 中的 typedef
Typedef in traits vs typedef in class
出于教育目的,我正在查看 Eigen 源代码。我注意到对于层次结构中的每个具体 class 模板 X
,都定义了一个 internal::traits<X>
。一个典型的例子可以在 Matrix.h:
中找到
namespace internal {
template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
struct traits<Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> >
{
typedef _Scalar Scalar;
typedef Dense StorageKind;
typedef DenseIndex Index;
typedef MatrixXpr XprKind;
enum {
RowsAtCompileTime = _Rows,
ColsAtCompileTime = _Cols,
MaxRowsAtCompileTime = _MaxRows,
MaxColsAtCompileTime = _MaxCols,
Flags = compute_matrix_flags<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::ret,
CoeffReadCost = NumTraits<Scalar>::ReadCost,
Options = _Options,
InnerStrideAtCompileTime = 1,
OuterStrideAtCompileTime = (Options&RowMajor) ? ColsAtCompileTime : RowsAtCompileTime
};
};
}
现在我明白 traits 是一种扩展现有 classes 的方式,您不想使用与某些新代码相关的额外信息来修改这些 es。例如,class 模板 Foo<class TAllocator>
的用户可能想利用现有的内存分配器 FastAlloc
和 AlignedAlloc
,但 Foo 需要知道如何与这两个接口,因此 FooTraits<AlignedAlloc>::allocate()
和 FooTraits<FastAlloc>::allocate()
由用户定义,而 Foo
又被 Foo
使用。
然而,在这种情况下,我并不容易看到仅在每个派生的 class 中指定 Scalar
的问题,即让 Matrix
定义 Matrix::Scalar
使用class 主体中的 typedef。这里使用特征 class 有什么好处?是否只是为了保持代码干净,即将每个 class 的所有相关属性存储在特征 class?
中
根据 Nicol Bolas 的回复进行编辑:我理解其中一些 typedef 可能需要保留 "internal",即不应该暴露给用户,这将解释特征 class。这似乎是有道理的,但是其中一些 typedef,例如 Scalar
、 是 通过 class 的基础中的 typedef 对外界可用=22=]:
template<typename Derived> class MatrixBase
: public DenseBase<Derived>
{
public:
typedef MatrixBase StorageBaseType;
typedef typename internal::traits<Derived>::StorageKind StorageKind;
typedef typename internal::traits<Derived>::Index Index;
typedef typename internal::traits<Derived>::Scalar Scalar;
typedef typename internal::packet_traits<Scalar>::type PacketScalar;
typedef typename NumTraits<Scalar>::Real RealScalar;
这让我们回到最初的问题:为什么 Scalar
不只是 Matrix
本身的一个类型定义?除了文体选择之外还有什么原因吗?
我怀疑,由于特征 class 是 internal
,这就是使用特征 class 的意义所在。也就是让这些东西内部。这样,Matrix
就没有很多奇怪的定义等,即使在其私有接口中也是如此。
考虑示例中的枚举。那些 "enums"(又名:C++11 之前的 static constexpr
变量)看起来不像是用户应该了解的任何内容。这是一个实现细节,因此应该隐藏它。
MatrixBase
的问题是CRTP问题。
看,Matrix
会这样定义:
class Matrix : public MatrixBase<Matrix>
这个部分定义导致两件事发生:
如果Matrix
还没有被声明为class类型,那么它就成为一个合法的class谁的名字可以被引用和使用。
模板 MatrixBase
必须用类型 Matrix
实例化。 现在.
这里的问题是"right now",Matrix
是一个不完整的class。编译器尚未进入该定义的主体,因此编译器对其内部结构一无所知。但是 MatrixBase
必须立即实例化。
因此,MatrixBase
不能使用其提供的 Derived
class 的任何 内容 。如果 Matrix
中有一些 typedef,MatrixBase<Derived>
无法 看到它。
现在,MatrixBase<Derived>
的成员函数可以查看 Derived
中的定义,因为它们是在完整的 class 定义之后定义的。即使那些函数是在 class.
的范围内定义的
但是您不能让 MatrixBase
的属性访问 Derived
的属性。因此,特征是间接的。特征 class 可以使用基于不完整类型的特化来向 MatrixBase
.
公开定义
traits
class 的主要原因是为了避免 CRTP 中的递归依赖。没有它,我们最终会得到类似的东西:
template <typename T>
struct Matrix : Base<Matrix<T>> {
typedef T Scalar;
};
template <typename Derived>
struct Base {
typename Derived::Scalar foo();
};
在某些情况下无法编译。
基本上,这个 class traits
允许在不知道 Matrix
.
的声明的情况下完全声明 Base<Matrix>
出于教育目的,我正在查看 Eigen 源代码。我注意到对于层次结构中的每个具体 class 模板 X
,都定义了一个 internal::traits<X>
。一个典型的例子可以在 Matrix.h:
namespace internal {
template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
struct traits<Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> >
{
typedef _Scalar Scalar;
typedef Dense StorageKind;
typedef DenseIndex Index;
typedef MatrixXpr XprKind;
enum {
RowsAtCompileTime = _Rows,
ColsAtCompileTime = _Cols,
MaxRowsAtCompileTime = _MaxRows,
MaxColsAtCompileTime = _MaxCols,
Flags = compute_matrix_flags<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::ret,
CoeffReadCost = NumTraits<Scalar>::ReadCost,
Options = _Options,
InnerStrideAtCompileTime = 1,
OuterStrideAtCompileTime = (Options&RowMajor) ? ColsAtCompileTime : RowsAtCompileTime
};
};
}
现在我明白 traits 是一种扩展现有 classes 的方式,您不想使用与某些新代码相关的额外信息来修改这些 es。例如,class 模板 Foo<class TAllocator>
的用户可能想利用现有的内存分配器 FastAlloc
和 AlignedAlloc
,但 Foo 需要知道如何与这两个接口,因此 FooTraits<AlignedAlloc>::allocate()
和 FooTraits<FastAlloc>::allocate()
由用户定义,而 Foo
又被 Foo
使用。
然而,在这种情况下,我并不容易看到仅在每个派生的 class 中指定 Scalar
的问题,即让 Matrix
定义 Matrix::Scalar
使用class 主体中的 typedef。这里使用特征 class 有什么好处?是否只是为了保持代码干净,即将每个 class 的所有相关属性存储在特征 class?
根据 Nicol Bolas 的回复进行编辑:我理解其中一些 typedef 可能需要保留 "internal",即不应该暴露给用户,这将解释特征 class。这似乎是有道理的,但是其中一些 typedef,例如 Scalar
、 是 通过 class 的基础中的 typedef 对外界可用=22=]:
template<typename Derived> class MatrixBase
: public DenseBase<Derived>
{
public:
typedef MatrixBase StorageBaseType;
typedef typename internal::traits<Derived>::StorageKind StorageKind;
typedef typename internal::traits<Derived>::Index Index;
typedef typename internal::traits<Derived>::Scalar Scalar;
typedef typename internal::packet_traits<Scalar>::type PacketScalar;
typedef typename NumTraits<Scalar>::Real RealScalar;
这让我们回到最初的问题:为什么 Scalar
不只是 Matrix
本身的一个类型定义?除了文体选择之外还有什么原因吗?
我怀疑,由于特征 class 是 internal
,这就是使用特征 class 的意义所在。也就是让这些东西内部。这样,Matrix
就没有很多奇怪的定义等,即使在其私有接口中也是如此。
考虑示例中的枚举。那些 "enums"(又名:C++11 之前的 static constexpr
变量)看起来不像是用户应该了解的任何内容。这是一个实现细节,因此应该隐藏它。
MatrixBase
的问题是CRTP问题。
看,Matrix
会这样定义:
class Matrix : public MatrixBase<Matrix>
这个部分定义导致两件事发生:
如果
Matrix
还没有被声明为class类型,那么它就成为一个合法的class谁的名字可以被引用和使用。模板
MatrixBase
必须用类型Matrix
实例化。 现在.
这里的问题是"right now",Matrix
是一个不完整的class。编译器尚未进入该定义的主体,因此编译器对其内部结构一无所知。但是 MatrixBase
必须立即实例化。
因此,MatrixBase
不能使用其提供的 Derived
class 的任何 内容 。如果 Matrix
中有一些 typedef,MatrixBase<Derived>
无法 看到它。
现在,MatrixBase<Derived>
的成员函数可以查看 Derived
中的定义,因为它们是在完整的 class 定义之后定义的。即使那些函数是在 class.
但是您不能让 MatrixBase
的属性访问 Derived
的属性。因此,特征是间接的。特征 class 可以使用基于不完整类型的特化来向 MatrixBase
.
traits
class 的主要原因是为了避免 CRTP 中的递归依赖。没有它,我们最终会得到类似的东西:
template <typename T>
struct Matrix : Base<Matrix<T>> {
typedef T Scalar;
};
template <typename Derived>
struct Base {
typename Derived::Scalar foo();
};
在某些情况下无法编译。
基本上,这个 class traits
允许在不知道 Matrix
.
Base<Matrix>