我想在我的程序中使用Eigen矩阵库作为线性代数引擎。 Eigen使用表达式模板来实现延迟评估并简化循环和计算。
例如:
#include<Eigen/Core>
int main()
{
int size = 40;
// VectorXf is a vector of floats, with dynamic size.
Eigen::VectorXf u(size), v(size), w(size), z(size);
u = 2*v + w + 0.2*z;
}
因为Eigen使用表达式模板,所以像
这样的代码u = 2*v + w + 0.2*z;
在上面提到的样本中,减少到长度为10的单个循环(不是40,浮动物被4个块放入调节器)而不会产生临时的。这有多酷?
但如果我像这样集成了库:
class UsingEigen
{
public:
UsingEigen(const Eigen::VectorXf& data):
data_(data)
{}
UsingEigen operator + (const UsingEigen& adee)const
{
return UsingEigen(data_ + adee.data_);
}
...
private:
Eigen::VectorXf data_;
}
然后表达式如下:
UsingEigen a, b, c, d;
a = b + c + d;
无法利用Eigen的实现方式。这不是最后一个。还有许多其他示例,其中表达模板用于Eigen。
简单的解决方案不是自己定义运算符,将data_
公开,只需编写如下表达式:
UsingEigen a, b, c, d;
a.data_ = b.data_ + c.data_ + d.data_;
这打破了封装,但它保留了Eigen的效率。
其他方式可以是创建自己的运算符,但让它们返回表达式模板。但由于我是C ++的初学者,我不知道这是否是正确的方法。
如果问题太笼统,我很抱歉。我是初学者,没有人问。到目前为止,我到处都在使用std::vector<float>
,但现在我也需要使用矩阵。在我的整个项目中从std::vector<float>
切换到Eigen是一个很大的进步,我害怕在一开始就拨打错误的电话。欢迎任何建议!
答案 0 :(得分:5)
为什么暴露data_
会破坏封装?封装意味着隐藏实现细节并仅暴露接口。如果您的包装类UsingEigen
未向本机Eigen
库添加任何行为或状态,则接口不会更改。在这种情况下,您应该完全删除此包装并使用Eigen
数据结构编写程序。
暴露矩阵或向量不会打破封装:只暴露矩阵或向量的实现就可以了。 Eigen
库公开算术运算符,但不公开它们的实现。
使用表达式模板库,用户扩展库功能的最常用方法是添加行为,而不是通过添加状态添加。对于添加行为,您不需要编写包装类:您还可以添加根据Eigen
类成员函数实现的非成员函数。请参阅Scott Meyers撰写的this column“非成员函数如何改进封装”。
至于您担心将当前程序转换为明确使用Eigen
功能的版本:您可以逐步执行更改,每次更改程序的一小部分,确保你的单元测试(你有单元测试,不是吗?)随着你的进展不要破坏。
答案 1 :(得分:2)
在我看来,这看起来更像是面向对象的设计问题而不是库使用问题。 无论你从书中读到什么都是正确的建议。即,不要暴露成员变量并保护上层免受第三方层使用的细微差别。
您可以期待的是可以在内部使用此库实现的数学函数的正确抽象。也就是说,您可以使用高级函数公开自己的库,而不是基本向量和矩阵运算。通过这种方式,您可以利用库对象之间交互的特性,同时您不必将成员变量暴露给上层。
例如,您可以抽象出更高级别的API,例如计算从点到平面的距离,两个平面之间的距离,使用变换矩阵计算另一个坐标系的点的新坐标等。实现这些方法在内部,您可以使用库对象。您可以限制不使用API签名中使用的任何库类,以避免依赖于此库中的上层。
程序的上层应该在抽象层次上更高,并且不必担心基本的实现细节,例如如何实现从点到平面的距离的计算等。此外,它们甚至不需要知道如果使用此库或其他方式实现此较低层。他们只会使用你的库的接口。
答案 2 :(得分:2)
设置一个类模板来保存一般的特征表达式,并使UsingEigen
成为它的特殊实例:
template<typename expr_t>
class UsingEigenExpr
{
UsingEigen(expr_t const& expr) : expr(expr) {}
expr_t expr;
operator UsingEigenExpr<Eigen::VectorXf>() const
{
return {expr};
}
};
using UsingEigen = UsingEigenExpr<Eigen::VectorXf>;
然后超载任何所需的功能,例如如
template<typename expr1_t, typename expr2_t, typename function_t>
auto binary_op(UsingEigenExpr<expr1_t> const& x, UsingEigenExpr<expr2_t> const& y, function_t function)
{
return UsingEigenExpr<decltype(function(std::declval<expr1_t>(),std::declval<expr2_t>()))>(function(x.expr,y.expr));
}
template<typename expr1_t, typename expr2_t>
auto operator+(UsingEigenExpr<expr1_t> const& x, UsingEigenExpr<expr2_t> const& y)
{
return binary_op(x,y,[](auto const& x, auto const& y) {return x+y;});
}
对于其他二元运算符(如operator-
)的等等,对于一元运算符,更常见的是对于您想要使用的所有其他东西。此外,您可以向UsingEigenExpr
添加一些其他成员函数,例如size()
,norm()
等
将其用作
UsingEigen b, c, d;
auto a = b + c + d;
存储表达式,或
UsingEigen b, c, d;
UsingEigen a = b + c + d;
直接评估它。
虽然这种方法有效,但最终您发现自己会复制所有必需的功能,因此请谨慎使用。
答案 3 :(得分:-2)
我不明白你的所有问题,我会尽力回答你。 在这句话中:
UsingEigen operator + (const UsingEigen& adee)const
{
return UsingEigen(data_ + adee.data_);
}
你有一个重载操作符(抱歉我不知道这是否是用英语写的正确方法),因此你可以这样写:
a = b + c + d;
而不是:
a.data_ = b.data_ + c.data_ + d.data_;
你不会有任何问题,你的程序费用也是一样的。此外,您将拥有封装和效率。
另一方面,如果你想定义自己的运算符,你可以像模板那样去做。您可以在网络上搜索“重载操作员”的信息,但与此类似:
UsingEigen operator + (const UsingEigen& adee)const
{
return UsingEigen(data_ + adee.data_);
}
您可以放置操作员并执行所需的操作,而不是“+”。
如果你想创建一个矩阵,那很简单。您只需要创建一个数组或向量矢量数组。
我认为是这样的:
std::vector<vector<float>>
我不确定但是很容易,另一方面你可以用这种方式使用一个简单的矩阵:
浮动YourMatrix [size] [size];
我希望它可以帮到你。我不明白你的所有问题,如果你需要更多的东西加我谷歌+我会尽力帮助你。
对不起我的英语,我希望你能理解所有这些并帮助你。