特征中的Typedef与类中的Typedef [英] Typedef in traits vs typedef in class

查看:71
本文介绍了特征中的Typedef与类中的Typedef的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在浏览Eigen源代码以用于教育目的.我注意到,对于层次结构中的每个具体的类模板 X ,都有一个 internal :: traits< X> 定义.一个典型的例子可以在Matrix.h中找到:

 命名空间内部{模板<类型名_Scalar,int _Rows,int _Cols,int _Options,int _MaxRows,int _MaxCols>结构特征< Matrix< _Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols>>{typedef _Scalar标量;typedef Dense StorageKind;typedef DenseIndex索引;typedef MatrixXpr XprKind;枚举{RowsAtCompileTime = _Rows,ColsAtCompileTime = _Cols,MaxRowsAtCompileTime = _MaxRows,MaxColsAtCompileTime = _MaxCols,标志= compute_matrix_flags< _Scalar,_Rows,_Cols,_Options,_MaxRows,_MaxCols> :: ret,CoeffReadCost = NumTraits< Scalar> :: ReadCost,选项= _选项,InnerStrideAtCompileTime = 1,OuterStrideAtCompileTime =(Options& RowMajor)吗?ColsAtCompileTime:RowsAtCompileTime};};} 

现在,我了解到特征是一种扩展现有类的方法,您可以使用与某些新代码有关的额外信息来扩展您不想修改的类.例如,类模板 Foo< class TAllocator> 的用户可能想利用现有的内存分配器 FastAlloc AlignedAlloc ,但是Foo需要知道如何与这两个接口,因此 FooTraits< AlignedAlloc> ::: allocate() FooTraits< FastAlloc> :: allocate()用户,然后由 Foo 使用.

但是,在这种情况下,我不容易看到仅在每个派生类中指定 Scalar 的问题,即让 Matrix 定义 Matrix ::在类主体中使用typedef的标量.使用traits类的好处是什么?仅仅是出于保持代码整洁的目的,即将每个类的所有相关属性存储在traits类中吗?

根据Nicol Bolas的回复进行我了解其中某些typedef可能需要保持在内部",即不应该向用户公开,这可以解释traits类.这似乎是有道理的,但是其中某些typedef(例如 Scalar )可以通过 Matrix :

  template<派生的类型名>MatrixBase类:公共DenseBase< Derived>{上市:typedef MatrixBase StorageBaseType;typedef类型名internal :: traits< Derived> :: StorageKind StorageKind;typedef typename internal :: traits< Derived> :: Index索引;typedef类型名internal :: traits< Derived> :: Scalar标量;typedef typename internal :: packet_traits< Scalar> :: type PacketScalar;typedef类型名NumTraits< Scalar> :: Real RealScalar; 

这使我们回到了最初的问题:为什么 Scalar 不只是 Matrix 本身中的typedef?除了风格选择外,还有什么原因吗?

我怀疑,由于traits类是 internal ,这就是使用traits类的目的.也就是说,将这些内容保留为内部.这样, Matrix 即使在其私有接口中也没有很多奇怪的定义.

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


MatrixBase 的问题是CRTP问题.

请参见,矩阵的定义如下:

  class Matrix:公共MatrixBase< Matrix> 

此部分定义导致2件事发生:

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

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

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

因此, MatrixBase 不能使用所提供的 Derived 类的任何 contents .如果 Matrix 中包含一些typedef,则 MatrixBase< Derived> 无法看到它.

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

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

I'm looking through the Eigen source code for educational purposes. I've noticed that for each concrete class template X in the hierarchy, there is an internal::traits<X> defined. A typical example can be found in 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
  };
};
}

Now I understand traits to be a way of extending existing classes that you do not want to modify with extra information pertaining to some piece of new code. For example, a user of class template Foo<class TAllocator> might want to make use of existing memory allocators FastAlloc and AlignedAlloc, but Foo needs to know how to interface with these two, and as such a FooTraits<AlignedAlloc>::allocate() and FooTraits<FastAlloc>::allocate() are defined by the user, which in turn are used by Foo.

In this case, however, I don't readily see the problem with just specifying Scalar in each derived class, i.e. have Matrix define Matrix::Scalar using a typedef in the class body. What is the advantage here of using a traits class? Is it just for the purposes of keeping the code clean, i.e. storing all relevant properties of each class in the traits class?

Edit as per Nicol Bolas's response: I understand that some of these typedefs might need to be kept "internal", i.e. should not be exposed to the user, which would explain the traits class. That seems to make sense, however some of these typedefs, such as Scalar, are available to the outside world, through a typedef in the base class of 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;

This brings us back to the original question: why isn't Scalar just a typedef in Matrix itself? Is there any reason aside from stylistic choice?

解决方案

I suspect that, since the traits class is internal, that this is the point of using a traits class. That is, to keep these things internal. That way, Matrix doesn't have a lot of oddball definitions and such, even in its private interface.

Consider the enumeration in your example. Those "enums" (aka: static constexpr variables before C++11) don't look like anything that a user should know about. It's an implementation detail, and therefore it should be hidden.


MatrixBase's problem is a CRTP issue.

See, Matrix would be defined like this:

class Matrix : public MatrixBase<Matrix>

This partial definition causes 2 things to happen:

  1. If Matrix has not already been declared as a class type, then it becomes a legal class who's name can be referenced and used.

  2. The template MatrixBase must be instantiated with the type Matrix. Right now.

The problem here is that "right now", Matrix is an incomplete class. The compiler has not yet entered the body of that definition, so the compiler doesn't know anything about its internals. But MatrixBase must be instantiated right now.

Therefore, MatrixBase cannot use any of the contents of the Derived class it is provided. If Matrix has some typedef in it, MatrixBase<Derived> cannot see it.

Now, member functions of MatrixBase<Derived> can look at definitions in Derived, because those are defined after the full class is defined. Even if those functions are defined within the scope of the class.

But you can't have properties of MatrixBase access properties of Derived. Hence the traits indirection. The traits class can use a specialization based on an incomplete type to expose defines to MatrixBase.

这篇关于特征中的Typedef与类中的Typedef的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆