具有 Eigen 的 TMP(模板元编程):一个简单的 * 二元运算符不能用扩展的 Eigen::Matrix 类型编译

TMP (Template meta-programming) with Eigen: A simple * binary operator does not compile with an extended Eigen::Matrix type

根据 Inheriting From Matrix 上扩展 Eigen::Types here 的示例,我创建了以下示例:

class MyType : public Eigen::Vector3d {
public:
  MyType(void) : Eigen::Vector3d() {}
  template<typename OtherDerived> // MyType ctor from Eigen expressions
  MyType(const Eigen::MatrixBase<OtherDerived>& other)
    : Eigen::Vector3d(other) {}

  // ... operator= as in the example, not shown to save some space ...

  // ONLY NEW CODE: a new binary operator ^ returning Matrix3d
  template<typename OtherDerived>
  Eigen::Matrix3d operator^ (const Eigen::MatrixBase<OtherDerived>& other) {
    return Eigen::Matrix3d{ *this * other.transpose() };
  }
};

新的二元运算符 ^ 仅根据条件执行其工作,如下所示:

MyType t1; t1.setRandom(); std::cout << "t1 is:\n" << t1 << std::endl;
MyType t2; t2.setRandom(); std::cout << "t2 is:\n" << t2 << std::endl;

Eigen::Matrix3d t3 = t1 ^ t2; std::cout << "t3 is:\n" << t3 << std::endl; // works
const double fac1{ 10.0 }; // invoke using multiplication factors
t3 = t1 ^ t2*fac1; // and t1 ^ fac1*t2 also works, output is correct

t3 = t1*fac1 ^ t2; // does not compile. Neither does t1*fac1 ^ t2.

我可以使用带有标量和 Vector3d 作为操作数的标准 * 二元运算符(由 Eigen 支持)。但它仅适用于我的新 ^ 二元运算符的第二个操作数。基于运算符优先级,也许还有一些 ADL(参数相关查找),我希望编译器将它们分类。现在这里是编译器错误消息(为了便于阅读,代码风格):

myCode.cpp(43) : error C2676: binary '^' :
  'const Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<double>,const Derived>'
  does not define this operator or a conversion to a type acceptable to the 
  predefined operator with
    [   Derived=Eigen::Matrix<double,3,1,0,3,1>    ]

我是否可以推断编译器以某种方式认为 t1*fac1 中的 * 是一元运算符?怎么在和t1类型一模一样的t2上应用就没有问题了。尝试在括号之间包含:(fac1*t1) ^ t2,但没有用。我还用一个标量和 MyType 作为操作数定义了我自己的 operator* (const double&),但是上面的代码行有完全相同的编译错误。

这可能是 Eigen 还是编译器错误?我正在使用 MS Visual C++ 2015。 我错过了一些简单的东西吗?感谢您就此问题提供的任何线索。

我认为你的问题抽象起来可能如下。在第一种情况下,t2*fac1 首先计算 returns 某个基本类型的临时对象,然后使用 t2 作为 调用 ^ 运算符this 是 MyType 类型,第二个参数是临时值。但是,在第二种情况下,您首先评估了 t1*fac1 并且 returns 也是某种基本类型的对象,但这次它试图在该基本类型对象上调用运算符 ^ 根据您的说法,该对象未在中实现这样的基础类型。

您说您实现了以 double 和 MyType 作为操作数的运算符 *,那是 class 成员运算符吗?也许您可以尝试类似的方法并确保在两种情况下都调用此运算符而不是基础 class:

中的那个
MyType operator*(const double&); // MyType as a member operator.

扩展 Eigen 的 API 的最佳方法不是通过子类化 Matrix,而是通过向 Eigen 的 DenseBaseMatrixBaseMatrix 添加方法类 通过 plugin mechanism 或通过实现模板化的自由函数。在您的情况下,最后一个选项是最简单的。使用 Eigen 3.3:

template<typename A,typename B>
Product<A,Transpose<const B> >
operator^(const MatrixBase<A> &a,const MatrixBase<B> &b) {
  return a * b.transpose();
}

大功告成。