【问题标题】:Copy templated function argument in Eigen在 Eigen 中复制模板化函数参数
【发布时间】:2023-03-12 06:00:01
【问题描述】:

我正在编写一个使用 Eigen 数据类型的泛型类。我已经在将构造函数参数分配给类成员变量时遇到了问题。我的代码的简化版本是:

template <typename Derived>
class A
{
public:
  Eigen::Matrix<Derived> M; // error C2976: too few template parameters

A(const Eigen::DenseBase<Derived> & V)
{
  M = V.eval(); // I would want to snapshot the value of V.
}
};

我现在的问题是M 应该是什么数据类型?我尝试了多种选择,例如:

Eigen::internal::plain_matrix_type_column_major<Derived> M;
Eigen::DenseBase<Derived> M;

但它们只会产生不同的错误。 请注意,我使用 C++17 并期望从构造函数中推断出类模板参数。

【问题讨论】:

  • '模板参数太少' – 如果 Eigen::Matrix 对您来说是正确的类型,那么请继续使用,但要找出 other parameters 的用途和适当地应用它们(提示:您可能还需要为自己的类提供更多模板参数)。
  • 你打算如何使用ADerived可以有哪些类型?如果您想将任何Derived 类型存储在相应的普通类型中(这会自动派生标量类型、尺寸等),您可以尝试Derived::PlainObject
  • 我希望由密集矩阵/数组导出。 V 可以有固定、动态或混合大小。 M 旨在仅存储 V 的值,这就是 .eval() 的原因。一般来说,A 将根据V 计算新值,但我需要保留输入和其他中间结果以供以后使用。
  • 您能否发布一个minimal reproducible example,说明您打算如何使用A?我猜你本质上是想写A a{some_expression};,对吧?

标签: c++ c++17 eigen template-argument-deduction class-template


【解决方案1】:

Eigen::Matrix 变量 M 的声明应该是这样的:

Eigen::Matrix<typename Derived::Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> M; 

然后您的代码将编译。见Demo

每个模板参数的详细说明可以在this link找到。

【讨论】:

  • 这当然是可能的,但是如果输入矩阵是固定大小的会发生什么呢?我认为在使一切动态化时,这些信息会丢失。
  • 您也可以指定整数值而不是Eigen::Dynamic。见:godbolt.org/z/JztB4N
  • 如果在构造类时知道矩阵大小,这将是最清晰的方法。就我而言,我打算编写一个可以采用固定大小和动态参数的类。编写课程时,我无法强制执行某些矩阵大小。
  • 您可以使用Derived::RowsAtCompileTime等。但您也可以简单地使用typename Derived::PlainObjecttypename Derived::PlainMatrix
【解决方案2】:

声明类型的通用方法是将生成它的声明用作源,这样您就不必查看声明复杂模板类型的特定方法,这是基于您的代码的示例:

decltype(static_cast<Eigen::DenseBase<Derived> *>(nullptr)->eval()) M;

不用担心这里没有 nullptr 取消引用,因为 decltype 里面的代码没有被执行。

正如 cmets 所指出的,有一种更简洁的写法:

decltype(declval<Eigen::DenseBase<Derived>>().eval()) M;

如果您担心该类型可能是引用并且不希望这样:

remove_reference_t<decltype(declval<Eigen::DenseBase<Derived>>().eval())> M;

也不要忘记 #include &lt;type_traits&gt; 并在所有内容前加上 std:: 或将 using namespace std; 添加到您的代码中。

为了使语法更简单以备将来使用,请将其添加到代码的开头:

template<typename T, typename T::f>
using member_function_return_t = remove_reference_t<decltype(declval<T>().f())>;

然后将变量声明为:

member_function_return_t<Eigen::DenseBase<Derived>, Eigen::DenseBase<Derived>::eval> M;

【讨论】:

  • 我对此进行了测试,它似乎有效。我不知道这种方法。我希望 Eigen 提供更直接的方法来获取矩阵或表达式的类型。
  • 如果 Derived 已经是 Matrix 类型,这将导致对该类型的 const 引用(可能是也可能不是 OP 想要的)。
  • 我相信引入std::declval 是为了避免这种结构;)
  • 在答案中包含了您的观察结果
  • 老实说,我没有看到与写typename Derived::PlainObject M相比有任何优势。
【解决方案3】:

您的容器需要实际的“普通类型”作为模板参数:

template <typename PlainType>
class A
{
    PlainType M; 
public:
    template<class Derived>
    A(const Eigen::MatrixBase<Derived> & V) : M(V) {}
};

而且还需要额外的模板扣减规则:

template<class Derived>
A(const Eigen::MatrixBase<Derived> & V) -> A<typename Derived::PlainObject>;

使用示例(on godbolt):

template<class X>
void bar(X&); // just to read full type of A

void foo(Eigen::Matrix2d const& M)
{
    A a = M*M;
    bar(a);  // calls bar<A<Matrix2d>>();
}

【讨论】:

    猜你喜欢
    • 2020-07-05
    • 2011-05-24
    • 2017-01-18
    • 2016-02-17
    • 1970-01-01
    • 2022-11-30
    • 2018-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多