创建派生 class 作为 superclass 的子集

Creating a derived class as a subset of superclass

我创建了一个模板矩阵 class。

我想做二维图形,所以我需要一个二维矢量。由于齐次坐标,这将包含三个项目。此外,我想要一个新的构造函数,我希望有人拥有 x 和 y 值,并且他能够构造一个 2D 点,而不必理解什么是模板化矩阵以及为什么这个向量中有 3 个元素。因此,我创建了一个派生的 class,因为这是我知道的唯一方法:

#include "matrix.hpp"

// 3 rows because of homogenous coordinate system.
class Point2D: public Matrix<3, 1>
{
    public:
        Point2D(float x, float y) : Matrix<3, 1>({x, y, 1}) {}
};

请注意派生的 class 更像是 Matrix<N,M> 的子集,它不添加任何功能,而是将模板化类型限制为 Matrix<3, 1>.

现在我想对我新建的Point2D进行操作:

    Point2D a(-1, 1);
    Matrix<3, 3> translate = {(1, 0, 2,
                               0, 1, -2,
                               0, 0, 1);

    Point2D out = translate*a;

这是不允许的:

g++ -I2dgfx/inc/ -Imatrix/inc/ -Iinc/ -std=gnu++20 -DDEBUG -Og -ggdb -Wall -Wshadow=local -Werror -Wno-error=unused-variable -Wno-error=unused-but-set-variable -MMD -MP -c 2dgfx/test/point2d.cpp -o obj/test/2dgfx/point2d.cpp.o
2dgfx/test/point2d.cpp: In member function ‘virtual void PointMatrix2D_translation_Test::TestBody()’:
2dgfx/test/point2d.cpp:23:28: error: conversion from ‘Matrix<3, 1>’ to non-scalar type ‘Point2D’ requested
   23 |     Point2D out = translate*a;
      |                   ~~~~~~~~~^~
make: *** [2dgfx/rules.mk:34: obj/test/2dgfx/point2d.cpp.o] Error 1

据我所知,Matrix<3,1>Point2D 不同。除了我希望这两个完全相等之外,我只需要那个新的构造函数来隐藏这样一个事实,即这个代表 2D space 中的点的对象秘密地有 3 个元素,其中最后一个是 1 因为数学。所以一个解决方案是修改 Point2D class:

#include "matrix.hpp"

// 3 rows because of homogenous coordinate system.
class Point2D: public Matrix<3, 1>
{
    public:
        Point2D(float x, float y) : Matrix<3, 1>({x, y, 1}) {}
        Point2D(const Matrix<3,1>& mat) : Matrix<3, 1>(mat) {}
};

哪个可行,但这是最好的方法吗?有没有更好的方法来实现我想要的?

Which works, but is this the best way? Is there a better way to achieve what I want?

最好的方法取决于您打算如何使用 Point2D

如果你真的希望Point2DMatrix<3,1>相同,一个可能的方法是使Point2D成为类型别名并编写一个make函数来创建它:

using Point2D = Matrix<3, 1>;
Point2D make_point_2d(float x, float y) { return Point2D{x, y, 1}; }

int main() {
    auto const a = make_point_2d(-1, 1);
    Matrix<3, 3> translate = {1, 0, 2, 0, 1, -2, 0, 0, 1};

    Point2D out = translate * a;
}

为了保持设计的一致性,我也会为其他类型引入类型别名和 make_* 函数。例如:

using Matrix3x3 = Matrix<3,3>;
Matrix3x3 make_matrix_3x3(/* ... */) { /*...*/ }

只要您需要引入的类型别名不是太多,这可能会起作用。


编辑: 我不太确定 Point2D 是(或公开继承自) Matrix<3,1> 是个好主意。作为用户,我会对这个事实感到非常惊讶 此代码将编译(并且 运行 不会导致任何 assert 失败):

auto const a = make_point_2d(-1, 1);
a(2,0);

// 1. Why do I need the second index to access the elements of a Point2D?
// 2. Why can I access the element at index 2?