Ваша функция явно возвращает объект Matrix
, поэтому нет, она не возвращает объект выражения. Один из способов исправить это - позволить компилятору определить для вас тип возвращаемого значения (поскольку объектом выражения будет некоторый гигантский c шаблонный беспорядок):
template <typename Derived>
auto SampleCov (DenseBase<Derived> const & x) {
auto const x_mean = x.colwise().mean();
return ((x.rowwise() - x_mean).matrix().transpose()
* (x.rowwise() - x_mean).matrix()) / (x.rows() - 1);
}
Это предполагает, что вы используете C + +14 или выше. Для C ++ 11, использующего только auto, поскольку тип возвращаемого значения не обрезает его, вам нужен конечный тип возвращаемого значения:
template <typename Derived>
auto SampleCov (DenseBase<Derived> const & x)
-> decltype(((x.rowwise() - x.colwise().mean()).matrix().transpose()
* (x.rowwise() - x.colwise().mean()).matrix()) / (x.rows() - 1)) {
auto const x_mean = x.colwise().mean();
return ((x.rowwise() - x_mean).matrix().transpose()
* (x.rowwise() - x_mean).matrix()) / (x.rows() - 1);
}
Обратите внимание, что я удалил RowVectorType
typedef, так как я не видел точка.
Что касается вопроса 2, я думаю, что приведенные выше примеры решают эту проблему, так как нет более явно названных типов. Об этом позаботится auto
как для возвращаемого типа, так и внутри функции.
Наконец, вопрос 3. Это зависит от того, что именно вы хотите сделать. Исходя из того, что вы говорите, кажется, что вышеупомянутые функции не работают для MatrixXd
объектов или объектов, возвращаемых путем вызова MatrixXd::block()
.
Для меня это на самом деле не означает, что вам нужно явное создание шаблона , о чем вы и просите.
Вместо этого вы можете просто сделать тип аргумента SampleCov
более обобщенным c:
template <typename T>
auto SampleCovGeneric (T const & x) {
auto const x_mean = x.colwise().mean();
return ((x.rowwise() - x_mean).matrix().transpose()
* (x.rowwise() - x_mean).matrix()) / (x.rows() - 1);
}
Пока вы можете вызывать colwise()
, matrix()
, ... для объекта типа T
, вы хороши для go. В любом случае, это, вероятно, лучший способ для go, поскольку теперь можно передавать выражения Eigen в SampleCovGeneric
. Например:
Eigen::MatrixXd a(2,2);
// aTranspose is NOT a matrix object!
// Its type is Eigen::Transpose<Eigen::Matrix<double, Dynamic, Dynamic>>.
auto aTranspose = a.transpose();
/* Note use of auto and eval() calls!
* See https://eigen.tuxfamily.org/dox/TopicPitfalls.html.
*/
auto b = SampleCov(aTranspose.eval()).eval();
auto c = SampleCovGeneric(aTranspose).eval();
В приведенном выше примере SampleCov
ожидает объект типа DenseBase<Derived>
. Но aTranspose
не такой тип. Таким образом, мы должны явно оценить (т.е. фактически рассчитать) транспонирование в первую очередь. В зависимости от того, что происходит внутри SampleCov
, это бесполезный расчет. Например, представьте, что в первую очередь вычисляется аргумент (x
) его транспонирования. В этом случае это будет просто оригинал. Но если транспонирование уже вычислено, то Эйген не может "увидеть" это и должен рассчитать транспонирование. С другой стороны,
SampleCovGeneric
принимает любой тип, поэтому нет необходимости оценивать aTranspose
первый. Это (возможно) позволяет Eigen "видеть" кучу вычислений и, таким образом, вычислять результат более оптимизированным способом. См., Например, https://eigen.tuxfamily.org/dox/TopicLazyEvaluation.html.
Если вам действительно нужно, вы можете явно создавать шаблоны , чтобы их можно было разделить на файлы заголовков и исходные файлы. Это будет go примерно так:
/* Header */
#pragma once
#include <utility>
// Reduce trailing return type mess a little
template <class T>
using SampleCovResultType = decltype(((std::declval<T const &>().rowwise()
- std::declval<T const &>().colwise().mean()).matrix().transpose()
* (std::declval<T const &>().rowwise()
- std::declval<T const &>().colwise().mean()).matrix())
/ (std::declval<T const &>().rows() - 1));
template <typename T>
auto SampleCov (T const & x) -> SampleCovResultType<T>;
// Explicit template declaration
extern template
auto SampleCov<Eigen::MatrixXd> (Eigen::MatrixXd const & x)
-> SampleCovResultType<Eigen::MatrixXd>;
/* Source */
#include "SampleCov.h"
template <class T>
auto SampleCov (T const & x) -> SampleCovResultType<T> {
auto const x_mean = x.colwise().mean();
return ((x.rowwise() - x_mean).matrix().transpose()
* (x.rowwise() - x_mean).matrix()) / (x.rows() - 1);
}
// Explicit template definition
template
auto SampleCov<Eigen::MatrixXd> (Eigen::MatrixXd const & x)
-> SampleCovResultType<Eigen::MatrixXd>;
Я не думаю, что вы можете избежать использования только auto в качестве возвращаемого типа. Поскольку определение функции помещается в отдельный исходный файл, компилятор не может видеть тело функции, поэтому он не может определить тип возвращаемого значения при вызове функции. Поэтому вы должны использовать конечный тип возврата.
Обратите внимание на следующее:
Явное объявление экземпляра (внешний шаблон) предотвращает неявное создание экземпляров: код, который в противном случае вызвал бы неявное создание должно использовать явное определение создания, предоставленное где-то еще в программе.
Т.е., если вы пытаетесь вызвать SampleCov
с типом T
, для которого вы явно не создали экземпляр SampleCov
, компиляция не удалась.
Примечание: Весь приведенный выше код не проверен. Вероятность опечаток высока:).
Правки:
- Исправление отсутствующего аргумента шаблона.
- Удалено замечание о вызове
matrix()
. Я задавался вопросом, оценивал ли он выражение, которое вы не хотите делать. AFAIK, это не так, на основании, например, документации ArrayBase . - Добавлена информация о явной реализации.