TMP (шаблон метапрограммирования) с Eigen: простой * бинарный оператор не компилируется с расширенным типом Eigen::Matrix

Следуя примеру для расширения Eigen::Types здесь, в разделе "Наследование от матрицы", я создал небольшое изменение примера:

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 такое унарный оператор? Почему это не было проблемой, когда он был применен на t2 чей тип точно такой же, как у t1, Пробовал вложить между паратезами так: (fac1*t1) ^ t2, но не сработало. Я также определил свой собственный operator* (const double&) со скаляром и MyType как операнды, но в приведенной выше строке кода была точно такая же ошибка компиляции.

Может ли это быть Eigen или ошибка компилятора? Я использую MS Visual C++ 2015. Я что-то упустил? Любой свет, который вы можете пролить на этот вопрос, приветствуется.

2 ответа

Решение

Лучший способ расширить API Eigen - это не создавать подклассы Matrix но либо добавив методы к Эйгена DenseBase, MatrixBase, или же Matrix классы через механизм плагинов или путем реализации шаблонных свободных функций. В вашем случае последний вариант самый простой. С 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();
}

И вы сделали.

Я думаю, что ваша проблема абстрактно может быть следующей. В первом случае, который работает, t2*fac1 сначала вычисляет и возвращает временный объект некоторого базового типа, а затем оператор ^ вызывается с t2, поскольку это имеет тип MyType, а второй аргумент является временным значением. Во втором случае, однако, сначала выполняется t1*fac1, и вы также возвращаете объект некоторого базового типа, но на этот раз он пытается вызвать оператор ^ для этого объекта базового типа, который, по вашему мнению, не реализован в таком базовом типе.

Вы сказали, что реализовали оператор * с двойным и MyType в качестве операндов, это был оператор члена класса? может быть, вы можете попробовать что-то подобное и убедиться, что этот оператор вызывается в обоих случаях, а не в базовом классе:

MyType operator*(const double&); // MyType as a member operator.
Другие вопросы по тегам