我正在查看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>
的用户可能想要使用现有的内存分配器FastAlloc
和AlignedAlloc
,但是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);
}
});
}
});
?除了风格选择之外还有什么理由吗?
答案 0 :(得分:6)
我怀疑,因为traits类是internal
,所以这是使用traits类的重点。也就是说,保持这些内容内部。这样,即使在私有界面中,Matrix
也没有很多古怪的定义。
考虑示例中的枚举。那些“枚举”(在C ++ 11之前称为static constexpr
变量)看起来不像用户应该知道的任何东西。这是一个实现细节,因此应该隐藏它。
MatrixBase
的问题是CRTP问题。
请参阅,Matrix
将定义如下:
class Matrix : public MatrixBase<Matrix>
这个部分定义会导致两件事情发生:
如果Matrix
尚未被声明为类类型,则它将成为可以引用和使用其名称的合法类。
必须使用类型MatrixBase
实例化模板Matrix
。 现在。
这里的问题是“现在”,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
。