特征中的Typedef与类中的Typedef [英] Typedef in traits vs typedef in class
问题描述
我正在浏览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?除了风格选择外,还有什么原因吗?
internal
,这就是使用traits类的目的.也就是说,将这些内容保留为内部.这样, Matrix
即使在其私有接口中也没有很多奇怪的定义.请考虑示例中的枚举.这些枚举"(又称C ++ 11之前的 static constexpr
变量)看起来并不像用户应该知道的任何东西.这是一个实现细节,因此应该隐藏.
MatrixBase
的问题是CRTP问题.
请参见,矩阵
的定义如下:
class Matrix:公共MatrixBase< Matrix>
此部分定义导致2件事发生:
-
如果尚未将
Matrix
声明为类类型,则它将成为可以引用和使用其名称的合法类. -
模板
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:
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.The template
MatrixBase
must be instantiated with the typeMatrix
. 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屋!