【问题标题】:Using CRTP to create Eigen matrix使用 CRTP 创建特征矩阵
【发布时间】:2019-02-03 02:20:10
【问题描述】:

我有一个包含一些特征矩阵作为成员的类层次结构,但它们的大小取决于派生类。我希望能够在基类中声明矩阵,但使用派生类的大小。我以为我可以为此使用 CRTP,但我不确定我是否正确使用它。这是我尝试过的代码

template<typename T>
class Base {
public:
    const int matSize = static_cast(T*)(this)->_matSize;
    Eigen::Matrix<int, matSize, mastSize> Mat = Eigen::Matrix<int, matSize, matSize>::Zero();

    virtual void print() { std::cout << Mat << std::endl; };
};

class Derived1 : public Base<Derived1>{
public:
    const int _matSize = 3;
};

class Derived2 : public Base<Derived2>{
public:
    const int _matSize = 4;
};

int main(){
    Derived1 d1;
    d1.print();   // print a 3x3 zero matrix

    Derived2 d2;
    d2.print();   // print a 4x4 zero matrix

    std::cin.get();
    return 0;
}

但是,这不起作用。有没有办法实现这样的目标?

编辑:

这样做的主要原因是我有一些函数可以做一些矩阵代数,无论大小如何,它都可以工作。所以我希望能够在不同派生类的对象上调用该函数,并且能够使用相同的函数,而不是为每个矩阵大小使用单独的函数。

还有一个接口,任何 Base 类型的对象都将有一个矩阵 Mat,其大小将取决于它是从哪个派生类 Base 创建的。

【问题讨论】:

  • 你必须学习关键字constexpr。
  • 能否请您详细说明或提供参考示例?我四处寻找 constexpr 在这种情况下的用法,但也许我不太了解 constexpr。
  • 我很好奇您为什么以这种方式使用 CRTP。您唯一提供的是矩阵大小,它是Eigen::Matrix 的模板参数,您可以简单地将d1 声明为Eigen::Matrix&lt;int,3,3&gt;::Zero()。你遗漏了什么吗?

标签: c++ templates eigen crtp


【解决方案1】:

正如我在评论中所说,确实没有理由将 CRTP 用于您所指出的内容,但如果您出于其他原因设置此模式,则类似以下内容应该可以工作(我没有 @987654321 @ 可用,所以我为编译器删除了必要的接口):

#include <iostream>

namespace Eigen {
    template<typename T, int W, int H>
    class Matrix {
    public:
        static Matrix<T,W,H> Zero() {
            return Matrix<T, W, H>{};
        }

        std::ostream &print_on(std::ostream &strm) const {
            return strm;
        }
    };
}

template <typename T, int W, int H>
std::ostream &operator<<(std::ostream &strm, Eigen::Matrix<T,W,H> const &matrix) {
    return matrix.print_on(strm);
}

template<typename T, int S>
class Base {
public:
    Eigen::Matrix<int, S, S> Mat = Eigen::Matrix<int, S, S>::Zero();

    virtual void print() { std::cout << Mat << std::endl; };
};

class Derived1 : public Base<Derived1,3>{
public:
};

class Derived2 : public Base<Derived2,4>{
public:
};

template <int Size>
class AdvertisingDerived : public Base<AdvertisingDerived<Size>,Size> {
public:
    constexpr static int matrixSize = Size;
};

int main(){
    Derived1 d1;
    d1.print();   // print a 3x3 zero matrix

    Derived2 d2;
    d2.print();   // print a 4x4 zero matrix

    AdvertisingDerived<3> ad1;

    AdvertisingDerived<4> ad2;

    std::cin.get();
    return 0;
}

【讨论】:

  • 效果很好!但是,我希望能够让派生类将矩阵大小“宣传”为static const int matrixSize = 3,然后在模板参数中将其用作class Derived1 : public Base&lt;Derived1, Derived1::matrixSize&gt;。但这给了我一个编译器错误invalid template argument for base, expected compile-time expression
  • 好的,我想我明白为什么会出现这个错误。即使变量matrixSize 是静态变量,编译器在创建类时编译器也无法使用它。我也理解你为什么认为 CRTP 是不必要的。我想我只是在尝试某种方式在派生类中设置矩阵大小并使用它来确定基类中矩阵的大小。
  • @Bilentor 是的,您必须记住在使用模板时编译时编程和运行时编程之间的区别,尤其是当您开始使用更复杂的技术时。不过,您可以通过模板元编程做的事情非常值得。
  • 感谢耐心的解释。我是来自 Python/Haskell 的 C++ 新手,所以仍然习惯于这些概念。
【解决方案2】:

在完全定义之前,您无法访问Derived1 的成员(从Derived1 之外的任何地方)。这个问题的通常解决方法是使用某种特征类:

template<class D>
struct MyTraits;

template<typename T>
class Base {
public:
    static const int matSize = MyTraits<T>::_matSize;
    Eigen::Matrix<int, matSize, matSize> Mat = Eigen::Matrix<int, matSize, matSize>::Zero();

    virtual void print() { std::cout << Mat << std::endl; };
};

class Derived1;
template<>
struct MyTraits<Derived1> {static const int _matSize = 3;};

class Derived1 : public Base<Derived1>{
public:
};

天箭:https://godbolt.org/z/pf-B_R

特别是,如果Derived1 本身是一个类模板,那么特征也会(部分)被模板化。从您提供的代码中很难判断这对您的情况是否有意义。

顺便说一句:应该不需要Base::print()virtual。静态多态的重点是避免动态多态。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-10
    • 2022-06-14
    • 1970-01-01
    相关资源
    最近更新 更多