特征中的Typedef与类

时间:2016-08-24 20:46:50

标签: c++ templates eigen traits

我正在查看Eigen源代码以用于教育目的。我注意到,对于层次结构中的每个具体类模板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是一种扩展现有类的方法,您不希望使用与某些新代码相关的额外信息来修改这些类。例如,类模板Foo<class TAllocator>的用户可能想要使用现有的内存分配器FastAllocAlignedAlloc,但是Foo需要知道如何与这两者进行交互,因此FooTraits<AlignedAlloc>::allocate()FooTraits<FastAlloc>::allocate()由用户定义,而Foo又使用了Scalar

但是,在这种情况下,我不容易在每个派生类中指定Matrix时看到问题,即在类体中使用typedef Matrix::Scalar定义Scalar 。使用特质类的优点是什么?它只是为了保持代码清洁,即在traits类中存储每个类的所有相关属性吗?

根据Nicol Bolas的回复进行编辑:我理解其中一些typedef可能需要保持“内部”,即不应该向用户公开,这将解释traits类。这似乎是有道理的,但是这些typedef中的一些,例如Matrix 可以通过基类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; 中的typedef对外界可用:

Scalar

这让我们回到原来的问题:Matrix为什么openInAppBrowser() { this.inAppBrowserRef=InAppBrowser.open("http://ws001.domeassistance.be:50001/", "_system", "location=no,fullscreen=yes,toolbar=no,clearcache=yes,clearsessioncache=yes"); } 本身不是var s3bucket = 'somebucket'; var s3Key = '/some/key', var body = fs.createReadStream('/some/local/file.txt'); var params = { Bucket: s3bucket, Key: s3Key, Body: body }; s3.upload(params, function(err) { if (err) { cb_1(err); } else { var params = { Bucket: s3bucket, Key: s3Key, Expires: parseInt(ttl) }; s3.getSignedUrl('getObject', params, function(err, url) { if (err) { console.log(err); } else { console.log(err); } }); } }); ?除了风格选择之外还有什么理由吗?

2 个答案:

答案 0 :(得分:6)

我怀疑,因为traits类是internal,所以这是使用traits类的重点。也就是说,保持这些内容内部。这样,即使在私有界面中,Matrix也没有很多古怪的定义。

考虑示例中的枚举。那些“枚举”(在C ++ 11之前称为static constexpr变量)看起来不像用户应该知道的任何东西。这是一个实现细节,因此应该隐藏它。

MatrixBase的问题是CRTP问题。

请参阅,Matrix将定义如下:

class Matrix : public MatrixBase<Matrix>

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

  1. 如果Matrix尚未被声明为类类型,则它将成为可以引用和使用其名称的合法类。

  2. 必须使用类型MatrixBase实例化模板Matrix现在

  3. 这里的问题是“现在”,Matrix是一个不完整的类。编译器尚未进入该定义的主体,因此编译器对其内部结构一无所知。但是MatrixBase必须立即实例化。

    因此,MatrixBase无法使用提供的Derived类的任何内容。如果Matrix中包含某个typedef,则MatrixBase<Derived> 无法看到它。

    现在,MatrixBase<Derived>的成员函数可以查看Derived中的定义,因为这些是在定义完整类之后定义的。即使这些函数是在类的范围内定义的。

    但您不能拥有MatrixBase Derived访问属性的属性。因此,特征是间接的。 traits类可以使用基于不完整类型的特化来将定义公开给MatrixBase

答案 1 :(得分:3)

这个traits类的主要原因是避免CRTP中的递归依赖。没有它,我们最终会得到类似的东西:

template <typename T>
struct Matrix : Base<Matrix<T>> {
  typedef T Scalar;
};
template <typename Derived>
struct Base {
  typename Derived::Scalar foo();
};

在某些情况下无法编译。 基本上,此类traits允许在不知道Base<Matrix>声明的情况下完全声明Matrix