特征中的 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> 的用户可能想利用现有的内存分配器 FastAllocAlignedAlloc,但 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>

这个部分定义导致两件事发生:

  1. 如果Matrix还没有被声明为class类型,那么它就成为一个合法的class谁的名字可以被引用和使用。

  2. 模板 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>