我基本上尝试编写自己的游戏引擎供练习和个人使用(我知道,这几乎是一项不可能完成的任务,但正如我所说,它主要用于学习新事物)。
目前,我正在研究我的数学库(主要是向量和矩阵),我遇到了一个有趣但主要是美学问题。
给出以下伪代码:
template <uint8 size>
struct TVector {
float elements[size];
};
现在我希望能够以所需的浮点数作为参数来构造结构:
TVector<3> vec0(1.0f, 2.5f, -4.0f);
TVector<2> vec1(3.0f, -2.0f);
TVector<3> vec2(2.0f, 2.2f); // Error: arg missing
TVector<2> vec3(1.0f, 2.0f, 3.0f) // Error: too many args
由于数组的大小是由模板参数给出的,所以我很难为结构声明一个合适的构造函数。我的最终目标是这样的:
// This is pseudo-ideal-code
TVector(size * (float value)); // Create a constructor with number of size
// parameters, which are all floats
当然,这是非逻辑语法,但我以这种方式实现的最接近的事情是使用C++17 折叠表达式:
template<typename... Args>
TVector(Args... values) {
static_assert(sizeof...(values) <= size, "Too many args");
uint8 i = 0;
(... , void(elements[i++] = values));
}
它在填充数组的意义上工作得非常好,而且(我猜)开销不大,但对于使用这个结构的程序员来说,它也容易出错,因为它没有直接指示构造函数接受了多少参数。
此外,它没有指定应该是哪种类型的参数,这是我最大的问题。
如果它有效,为什么会有问题?
想象一下使用 TVector 结构的以下结构:
template <const uint8 rows, const uint8 columns>
struct TMatrix {
// elements[-columns-][-rows-];
TVector<rows> elements[columns];
}
鉴于构造函数类似于向量结构的折叠表达式,我希望能够使用相应大小的向量或大括号初始化来构造矩阵。
聚合初始化不起作用。
TVector<2> vec(1.0f, 3.0f); TMatrix<2, 2> mat0(vec, vec); // Works TMatrix<2, 2> mat1(vec, {0.2f, -4.2f}); // Error // Does not compile, because the Type is not clear
当给定错误的参数(例如大小错误的向量,不适合作为矩阵的列)时,它在编译之前不会显示错误。
错误的来源并不总是很清楚。
TL; DR:现在终于到了我真正的问题:
有没有办法限制折叠表达式的类型,最终根本不使用模板并解决我上面给出的 3 个问题?
我想像:
TVector(float... values) {
// Maybe even specify the size of the pack with the size given in the struct template
uint8 i = 0;
(... , void(elements[i++] = values));
}
和:
TMatrix(const TVector<rows>&... values) {
uint8 i = 0;
(..., void(elements[i++] = values));
}
当然,我在这里很挑剔,这主要是一个审美问题,但我认为这是一个重要的设计决策,它可以真正提高代码的可用性。
感谢您阅读本文并帮助我解决我的第一个问题 :)