我正在使用Eigen编写CFD应用程序进行大量计算。现在,我想定义一个字段类来保存每个变量的值。我目前的方法是使用一个模板类,该模板类针对标量和向量实例化,或者由ScalarField和VectorField类继承,然后进行专门化处理。标量存储在1 x n矩阵中,向量存储在2 x n矩阵中(在2D中)。
现在,当我想访问这些字段的单个标量或(列)向量时,会出现问题,因为Eigen的访问函数的返回类型是表达式模板,而不是实例化模板所用的数据类型。但是我需要从基类内部访问它们,否则我将有很多重复的代码。
因此,最后,我发现了两种可行的方法,都涉及重载下标运算符[]
:
基类模板中的纯虚函数,返回类型由std::conditional
决定,基于实例化模板所使用的数据类型,然后在派生类中具有相当可读的覆盖,以及] >
使用SFINAE使用std::enable_if
定义基类模板内的所有函数,从而完全避免继承。但是那些模板函数不是很可读。
作为向量的operator[]
的返回类型,我使用了Eigen :: Block <...>顺便说一下。
这是一个最小的工作示例,其中get()和get2()函数分别代表虚函数方法和SFINAE方法,标量和向量分别为int
和double
,因此它可以易于编译。
#include <iostream> #include <type_traits> template<typename T> class Base { protected: T m_data; public: Base(T t) : m_data {t} {} virtual typename std::conditional< std::is_same<T, double>::value, int, T>::type get() = 0; template<typename U = T> std::enable_if_t<std::is_integral<U>::value,T> get2(){ return m_data; } template<typename U = T> std::enable_if_t<std::is_floating_point<U>::value,int> get2(){ return m_data; } void print() { std::cout << this->get() << "\n"; std::cout << this->get2() << "\n"; } }; class Int : public Base<int> { public: Int(int i) : Base(i) {} virtual int get() override { return m_data; } }; class Double : public Base<double> { public: Double(double d) : Base<double>(d) {} virtual int get() override { return m_data; } }; int main() { Int i { 1 }; i.print(); Double d { 1.1 }; d.print(); //Base<int> b1 { 1 }; //b1.print(); //Base<double> b2 { 1.1 }; //b2.print(); }
现在我的问题是:有没有一种更优雅,更可维护的方式来实现我的模板专业化目标(在当前情况下,访问单个元素)?如果不是,上述方法之一是可取的还是可以改善的?欢迎任何反馈,我对C ++还是陌生的。
编辑:根据要求,这与我的实际代码更相似。
我在setBoundaryPatch()
和setInternalField
中使用下标运算符,并且它们的重载用于访问m_data
。因为对于标量,m_data[]
可以正常工作,但是对于矢量或块,我需要使用m_data.col()
或m_data.block()
。
我只是尝试对标量和向量使用m_data.col()
或m_data.block()
,但是没有从标量到相应表达式的转换。我想我可以使用m_data.block(...) = T { value }
,但是那样会不会为每个调用构造一个临时对象并且速度很慢?
此外,重载下标运算符也使得使用该类非常方便。
#include <Eigen/Dense>
#include <type_traits>
#include "mesh.h"
namespace fvm
{
constexpr auto dims { 2 }; // dimensions
using vector = Eigen::Matrix<double, dims, 1>;
using field = Eigen::Matrix<double, n, Eigen::Dynamic>;
template<typename T>
class Field
{
protected:
static constexpr int n {
(std::is_same<T, double>::value ? 1 : fvm::dims ) };
/* because of some more member variables than just the data, e.g. a
* reference to the mesh, I don't want to use an Eigen type directly. */
const fvm::Mesh& m_mesh;
fvm::field<n> m_data;
public:
Field( const fvm::Mesh& mesh ) :
m_mesh { mesh }
{
m_data.resize( Eigen::NoChange, m_mesh.cellCount() );
}
const fvm::field<n>& data() const
{ return m_data; }
fvm::field<n>& data()
{ return m_data; }
/* sets the field values belonging to the ghost cells of a boundary patch to
* a uniform value */
void setBoundaryPatch( const std::string& patchName, T )
{
for ( auto faceInd : m_mesh.boundaryPatch(patchName) )
(*this)[ m_mesh.face(faceInd).ghostCell().ID() ] = t;
}
/* and a couple overloads for non-uniform values */
/* sets the field values belonging to domain cells to a uniform value */
void setInternalField( T );
/* and a couple overloads for non-uniform values */
protected:
using col = Eigen::Block< fvm::field<n> >;
using constCol = const Eigen::Block< const fvm::field<n> >;
public:
/* using SFINAE to define subscript operator[] */
//template<typename U = T>
//std::enable_if_t<!std::is_same<U, double>::value, constCol>
//operator[] (int i) const {
// return m_data.block(0, i, n, 1);
//}
//template<typename U = T>
//std::enable_if_t<!std::is_same<U, double>::value, col>
//operator[] (int i) {
// return m_data.block(0, i, n, 1);
//}
//template<typename U = T>
//std::enable_if_t<std::is_same<U, double>::value, T>
//operator[] (int i) const {
// return m_data[i];
//}
//template<typename U = T>
//std::enable_if_t<std::is_same<U, double>::value, T&>
//operator[] (int i) {
// return m_data[i];
//}
/* using pure virtual functions to overload the subscript operator[] */
virtual typename std::conditional<
std::is_same<T, fvm::vector>::value, constCol, T>::type
operator[] (int) const = 0;
virtual typename std::conditional<
std::is_same<T, fvm::vector>::value, col, T&>::type
operator[] (int) = 0;
virtual void readFromFile( const std::string& path ) = 0;
virtual void writeToFile( const std::string& path ) const = 0;
};
/* if I defined everything in the template, I could just declare aliases
* -> SFINAE option*/
//using ScalarField = Field<fvm::scalar>;
//using VectorField = Field<fvm::vector>;
/* or I define them as classes and let them inherit from Field<> */
class ScalarField : public Field<fvm::scalar>
{
public:
ScalarField( const fvm::Mesh& mesh );
virtual fvm::scalar operator[] (int) const override;
virtual fvm::scalar& operator[] (int) override;
virtual void writeToFile( const std::string& path ) const override;
virtual void readFromFile( const std::string& path ) override;
};
class VectorField : public Field<fvm::vector>
{
public:
VectorField( const fvm::Mesh& );
virtual VectorField::constCol operator[] (int) const override;
virtual VectorField::col operator[] (int) override;
virtual void writeToFile( const std::string& path ) const override;
virtual void readFromFile( const std::string& path ) override;
};
} // end namespace fvm
我正在使用Eigen编写CFD应用程序进行大量计算。现在,我想定义一个字段类来保存每个变量的值。我当前的方法是使用一个模板类,......>
我仍然非常乐于回答和反馈,但是,我至少使用if constexpr
,找到了比以前更冗长,更优雅的解决方案(C ++ 17非常棒!):] >
using col = Eigen::Block< fvm::field<n> >;
using constCol = const Eigen::Block< const fvm::field<n> >;
using elem = typename std::conditional<n==1, fvm::scalar&, col >::type;
using constElem = typename std::conditional<n==1, fvm::scalar , constCol>::type;
elem operator[] (int) {
if constexpr ( n==1 )
return m_data[i];
else
return m_data.block(0, i, n, 1);
}
constElem operator[] (int) const {
if constexpr ( n==1 )
return m_data[i];
else
return m_data.block(0, i, n, 1);
}