Dense
和Sparse
矩阵类型分别编写重复的函数。下面的示例显示了一个
add
函数。其中包括两个函数的工作示例,以及两次失败的尝试。代码示例的godbolt链接在下面提供。我查看了Eigen文档中关于编写接受Eigen类型的函数的部分,但是它们使用
Eigen::EigenBase
的方法不可行,因为MatrixBase
和SparseMatrixBase
都有特定的方法可用,而这些方法在EigenBase
中不存在。
https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html
我们使用C++14,非常感谢您的帮助和时间!!#include <Eigen/Core>
#include <Eigen/Sparse>
#include <iostream>
// Sparse matrix helper
using triplet_d = Eigen::Triplet<double>;
using sparse_mat_d = Eigen::SparseMatrix<double>;
std::vector<triplet_d> tripletList;
// Returns plain object
template <typename Derived>
using eigen_return_t = typename Derived::PlainObject;
// Below two are the generics that work
template <class Derived>
eigen_return_t<Derived> add(const Eigen::MatrixBase<Derived>& A) {
return A + A;
}
template <class Derived>
eigen_return_t<Derived> add(const Eigen::SparseMatrixBase<Derived>& A) {
return A + A;
}
int main()
{
// Fill up the sparse and dense matrices
tripletList.reserve(4);
tripletList.push_back(triplet_d(0, 0, 1));
tripletList.push_back(triplet_d(0, 1, 2));
tripletList.push_back(triplet_d(1, 0, 3));
tripletList.push_back(triplet_d(1, 1, 4));
sparse_mat_d mat(2, 2);
mat.setFromTriplets(tripletList.begin(), tripletList.end());
Eigen::Matrix<double, -1, -1> v(2, 2);
v << 1, 2, 3, 4;
// Works fine
sparse_mat_d output = add(mat * mat);
std::cout << output;
// Works fine
Eigen::Matrix<double, -1, -1> output2 = add(v * v);
std::cout << output2;
}
我希望有一个函数可以同时处理稀疏矩阵和密集矩阵的加法,而不是两个不同的函数。但是下面的尝试并没有成功。
模板模板类型
这是我显然很失败的一次尝试,用模板模板类型替换上述的两个add
函数会导致一个模糊的基类错误。
template <template <class> class Container, class Derived>
Container<Derived> add(const Container<Derived>& A) {
return A + A;
}
错误:
<source>: In function 'int main()':
<source>:35:38: error: no matching function for call to 'add(const Eigen::Product<Eigen::SparseMatrix<double, 0, int>, Eigen::SparseMatrix<double, 0, int>, 2>)'
35 | sparse_mat_d output = add(mat * mat);
| ^
<source>:20:20: note: candidate: 'template<template<class> class Container, class Derived> Container<Derived> add(const Container<Derived>&)'
20 | Container<Derived> add(const Container<Derived>& A) {
| ^~~
<source>:20:20: note: template argument deduction/substitution failed:
<source>:35:38: note: 'const Container<Derived>' is an ambiguous base class of 'const Eigen::Product<Eigen::SparseMatrix<double, 0, int>, Eigen::SparseMatrix<double, 0, int>, 2>'
35 | sparse_mat_d output = add(mat * mat);
| ^
<source>:40:52: error: no matching function for call to 'add(const Eigen::Product<Eigen::Matrix<double, -1, -1>, Eigen::Matrix<double, -1, -1>, 0>)'
40 | Eigen::Matrix<double, -1, -1> output2 = add(v * v);
| ^
<source>:20:20: note: candidate: 'template<template<class> class Container, class Derived> Container<Derived> add(const Container<Derived>&)'
20 | Container<Derived> add(const Container<Derived>& A) {
| ^~~
<source>:20:20: note: template argument deduction/substitution failed:
<source>:40:52: note: 'const Container<Derived>' is an ambiguous base class of 'const Eigen::Product<Eigen::Matrix<double, -1, -1>, Eigen::Matrix<double, -1, -1>, 0>'
40 | Eigen::Matrix<double, -1, -1> output2 = add(v * v);
| ^
我认为这是来自这里的相同的钻石继承问题:
https://www.fluentcpp.com/2017/05/19/crtp-helper/
使用 std::conditional_t
下面的示例尝试使用 conditional_t
推断正确的输入类型。
#include <Eigen/Core>
#include <Eigen/Sparse>
#include <iostream>
// Sparse matrix helper
using triplet_d = Eigen::Triplet<double>;
using sparse_mat_d = Eigen::SparseMatrix<double>;
std::vector<triplet_d> tripletList;
// Returns plain object
template <typename Derived>
using eigen_return_t = typename Derived::PlainObject;
// Check it Object inherits from DenseBase
template<typename Derived>
using is_dense_matrix_expression = std::is_base_of<Eigen::DenseBase<std::decay_t<Derived>>, std::decay_t<Derived>>;
// Check it Object inherits from EigenBase
template<typename Derived>
using is_eigen_expression = std::is_base_of<Eigen::EigenBase<std::decay_t<Derived>>, std::decay_t<Derived>>;
// Alias to deduce if input should be Dense or Sparse matrix
template <typename Derived>
using eigen_matrix = typename std::conditional_t<is_dense_matrix_expression<Derived>::value,
typename Eigen::MatrixBase<Derived>, typename Eigen::SparseMatrixBase<Derived>>;
template <typename Derived>
eigen_return_t<Derived> add(const eigen_matrix<Derived>& A) {
return A + A;
}
int main()
{
tripletList.reserve(4);
tripletList.push_back(triplet_d(0, 0, 1));
tripletList.push_back(triplet_d(0, 1, 2));
tripletList.push_back(triplet_d(1, 0, 3));
tripletList.push_back(triplet_d(1, 1, 4));
sparse_mat_d mat(2, 2);
mat.setFromTriplets(tripletList.begin(), tripletList.end());
sparse_mat_d output = add(mat * mat);
std::cout << output;
Eigen::Matrix<double, -1, -1> v(2, 2);
v << 1, 2, 3, 4;
Eigen::Matrix<double, -1, -1> output2 = add(v * v);
std::cout << output2;
}
这会抛出错误
<source>: In function 'int main()':
<source>:94:38: error: no matching function for call to 'add(const Eigen::Product<Eigen::SparseMatrix<double, 0, int>, Eigen::SparseMatrix<double, 0, int>, 2>)'
94 | sparse_mat_d output = add(mat * mat);
| ^
<source>:79:25: note: candidate: 'template<class Derived> eigen_return_t<Derived> add(eigen_matrix<Derived>&)'
79 | eigen_return_t<Derived> add(const eigen_matrix<Derived>& A) {
| ^~~
<source>:79:25: note: template argument deduction/substitution failed:
<source>:94:38: note: couldn't deduce template parameter 'Derived'
94 | sparse_mat_d output = add(mat * mat);
| ^
<source>:99:52: error: no matching function for call to 'add(const Eigen::Product<Eigen::Matrix<double, -1, -1>, Eigen::Matrix<double, -1, -1>, 0>)'
99 | Eigen::Matrix<double, -1, -1> output2 = add(v * v);
| ^
<source>:79:25: note: candidate: 'template<class Derived> eigen_return_t<Derived> add(eigen_matrix<Derived>&)'
79 | eigen_return_t<Derived> add(const eigen_matrix<Derived>& A) {
| ^~~
<source>:79:25: note: template argument deduction/substitution failed:
<source>:99:52: note: couldn't deduce template parameter 'Derived'
99 | Eigen::Matrix<double, -1, -1> output2 = add(v * v);
这似乎是因为依赖类型的相关参数不能像链接中所述那样被推导出来。
Godbolt示例
下面的Godbolt示例包含了以上所有实例,可以进行操作
有没有仅使用一个函数而不是两个的方法?我们有很多既支持稀疏矩阵又支持密集矩阵的函数,因此避免代码重复将是很好的选择。
编辑:可能的答案
@Max Langhof建议使用
template <class Mat>
auto add(const Mat& A) {
return A + A;
}
auto
关键字在Eigen中有一定的危险性。
https://eigen.tuxfamily.org/dox/TopicPitfalls.html
但是
template <class Mat>
typename Mat::PlainObject add(const Mat& A) {
return A + A;
}
虽然说实话我不完全确定为什么在这种情况下返回一个普通对象是可行的
编辑编辑
有几个人提到了使用auto
关键字。但遗憾的是,Eigen与auto
不兼容,可以参考下面链接中关于C++11和auto的第二部分。
https://eigen.tuxfamily.org/dox/TopicPitfalls.html
有些情况下可以使用auto,但我想看看是否存在一种通用的“auto”方式,适用于Eigen的模板返回类型。
如果您想要一个关于使用auto导致段错误的示例,您可以尝试将add替换为
template <typename T1>
auto add(const T1& A)
{
return ((A+A).eval()).transpose();
}
template <class Mat> auto add(const Mat& A) { return A + A; }
(可能需要一些SFINAE来限制它只能用于Eigen矩阵),有什么特别的原因吗?或者这样会遇到表达式模板的问题吗? - Max Langhofauto
关键字,因为 Eigen 在他们的文档中说不要使用它。https://eigen.tuxfamily.org/dox/TopicPitfalls.html - Steve Bronder