在Eigen中正确模板初始化

时间:2017-11-23 22:04:00

标签: c++ templates eigen

关于Eigen中适当的参数类型推导。

我想创建一个可以用Matrix(MatrixXd)或Vector(VectorXd)初始化的类。问题是MatrixXdVectorXd在Eigen中都是typedefs

// roughly
typedef Matrix<double, Dynamic, 1> VectorXd;
typedef Matrix<double, Dynamic, Dynamic> MatrixXd;

如果我尝试使用VectorXdMatrixXd重载两个构造函数,则会出现Ambiguous Constructor call错误。

考虑以下示例:

#include <iostream>
#include <Eigen/Dense>

using namespace std;
using namespace Eigen;

class Container {
public:
    VectorXd a;
    MatrixXd b;

    Container(VectorXd a): a(a), b(MatrixXd::Zero(3,3)) {
        cout << "Initializing with vector" << endl;
    }

    Container(MatrixXd b): a(VectorXd::Zero(3)), b(b) {
        cout << "Initializing with matrix" << endl;
    }
};

int main() {
    Container x(VectorXd::Ones(4));
    cout << x.a << endl;
    cout << x.b << endl;

    Container y(MatrixXd::Ones(4, 4));
    cout << y.a << endl;
    cout << y.b << endl;

    return 0;
}

我得到的确切错误:

main.cpp:23:15: error: call to constructor of 'Container' is ambiguous
Container x(Matrix<double, Dynamic, 1>::Ones(4));
          ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:13:5: note: candidate constructor
Container(Matrix<double, Dynamic, 1> a): a(a), b(MatrixXd::Zero(3,3)) {
^
main.cpp:17:5: note: candidate constructor
Container(Matrix<double, Dynamic, Dynamic> b): a(VectorXd::Zero(3)), b(b) {

所以问题是:我怎么能解决我想要调用的构造函数?

2 个答案:

答案 0 :(得分:1)

完美转发,sfinae约束构造函数适用于您的特定用例:

template<class T>
Container(T&& a,decltype(VectorXd{std::forward<T>(a)})* = 0): a(std::forward<T>(a)), b(MatrixXd::Zero(3,3)) {}

template<class T>
Container(T&& b,decltype(MatrixXd{std::forward<T>(a)})* = 0): a(VectorXd::Zero(3)), b(std::forward<T>(b)){}

但我建议不要使用它(或任何等效的),因为不能保证这适用于任何矩阵表达式或未来Eigen版本中的这些表达式,因为这些转换不能保证据我所知,是友好的。

我认为提供更合理的重载(取决于您的实际使用情况)会更好,或许类似

class Container {
public:
    VectorXd a;
    MatrixXd b;

    struct DefaultedT{};
    static constexpr DefaultedT Defaulted{};

    Container(VectorXd a,MatrixXd b): a(std::move(a)), b(std::move(b)) {}
    Container(VectorXd a,DefaultedT): a(std::move(a)), b(MatrixXd::Zero(3,3)) {}
    Container(DefaultedT,MatrixXd b): a(VectorXd::Zero(3)), b(std::move(b)) {}
};

int main() {
    Container x(VectorXd::Ones(4),Container::Defaulted);
    Container y(Container::Defaulted,MatrixXd::Ones(4, 4));
    ...

或者只是编写一个构造函数来获取矩阵表达式并相应地执行初始化逻辑...

答案 1 :(得分:1)

这似乎是一个非常糟糕的设计,我强烈建议重新访问它,也许使用具有足够名称的setter。例如,如果你传递一个MatrixXd(4,1)??

怎么办?

无论如何,要回答这个问题,你可以使用sfinae来检测输入是编译时的向量还是矩阵:

template<typename Derived>
Container(const MatrixBase<Derived> &a, typename std::enable_if<Derived::IsVectorAtCompileTime,void*>::type* = 0): a(a), b(MatrixXd::Zero(3,3)) {
    cout << "Initializing with vector" << endl;
}

template<typename Derived>
Container(const MatrixBase<Derived> &b, typename std::enable_if<!Derived::IsVectorAtCompileTime,void*>::type* =0): a(VectorXd::Zero(3)), b(b) {
    cout << "Initializing with matrix" << endl;
}