我正在写一个数学库供培训。现在,我正在研究Matrix类。
我正在尝试制作一个identityMatrix()
函数。这是一个模板函数,可生成并返回一个单位矩阵。现在我的代码是:
template<index_t N, typename Scalar = double>
constexpr inline static const Matrix<N, N, Scalar> identityMatrix() noexcept
{
constexpr static Matrix<N, N, Scalar> id = []
{
Matrix<N, N, Scalar> m(0);
for (index_t i = 0; i < N; i++)
{
m.at(i, i) = static_cast<Scalar>(1.0);
}
return m;
}();
return id;
}
我正在尝试使它仅在需要时才构建一次矩阵,以后对该函数的任何引用都将返回一个函数局部静态const对象。由于初始化过程很复杂,因此我要使用lambda并立即调用它。
Matrix
定义如下:
using index_t = uint32_t;
#define FOR(i, N) for(index_t i = 0; i < N; ++i)
#define FORiN(N) FOR(i, N)
template<index_t ROWS, index_t COLS = ROWS, typename Scalar = double>
struct Matrix
{
static constexpr auto BUFFER_SIZE = ROWS * COLS;
Scalar buffer[BUFFER_SIZE];
//...
constexpr Matrix() noexcept = default;
constexpr Matrix(const Scalar (&init_data)[BUFFER_SIZE]) noexcept
{
memcpy_s(buffer, BUFFER_SIZE * sizeof Scalar, &init_data , BUFFER_SIZE * sizeof Scalar);
}
constexpr Matrix(const std::initializer_list<std::initializer_list<Scalar>>& init_data)
{
static_assert(init_data.size() == ROWS && init_data.begin()->size() == COLS);
FOR(row_index, ROWS)
{
Scalar* src_row = &init_data.begin()[row_index];
Scalar* dst_row = &buffer[row_index * COLS];
constexpr index_t row_size_bytes = COLS * sizeof Scalar;
memcpy_s(dst_row, row_size_bytes, src_row, row_size_bytes);
}
}
constexpr Matrix(Scalar homogenuos_value) noexcept
{
std::fill_n(buffer, BUFFER_SIZE, homogenuos_value);
}
constexpr Matrix(const Matrix& rhs) noexcept
{
FOR(i, BUFFER_SIZE)
buffer[i] = rhs.buffer[i];
}
inline constexpr static index_t indexOf(index_t col, index_t row) { return row * COLS + col; }
inline constexpr Scalar& at(index_t row, index_t col) { return buffer[indexOf(row, col)]; }
inline constexpr const Scalar& at(index_t row, index_t col) const { return buffer[indexOf(row, col)]; }
//... more operators and stuff
};
我要测试矩阵代码的测试代码如下:
constexpr auto id = identityMatrix<3>();
constexpr auto manual_id = Matrix<3, 3>({ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 });
static_assert(id == manual_id);
print(id);
为什么这不起作用?我已经使一切 constexpr
在编译时应该保持不变。编译器应该能够做到这一点。我想念什么?我在做什么错了?
我正在使用
Microsoft Visual Studio Professional 2019-版本16.4.2
平台工具集:Visual Studio 2019(v142)
C ++语言标准:预览-最新C ++工作草案(std:c ++ latest)的功能
今天的日期:2020年6月9日
答案 0 :(得分:1)
这是因为函数memcpy_s
不是constexpr函数。
当编译器解析模板化的constexpr函数时,它不会检查那些函数是否为constexpr。而是,在这些函数的实例化时检查constexprness。如果这些实例不是constexpr,则不是错误。
这就是这里发生的情况,Matrix<3,3>
构造函数都不是constexpr,因为memcpy_s
不是constexpr。
答案 1 :(得分:0)
为什么这不起作用?我已经将所有constexpr都设置为在编译时应保持不变。编译器应该能够做到这一点。我想念什么?我在做什么错了?
没有完整的示例(index_t
是什么?FOR()
是什么?等等),很难准确地说出来,但是...
(1)constexpr
构造函数必须初始化非静态数据成员。我的意思是:在初始化列表中。
所以
constexpr Matrix(Scalar homogenuos_value) noexcept
是错误的,因为您必须初始化buffer
// ..................................................vvvvvvvv
constexpr Matrix(Scalar homogenuos_value) noexcept : buffer{}
这也适用于其他构造函数
(2)constexpr
函数不能调用(或不能在编译时执行)非constexpr
函数。
但是您可以在std::fill_n()
构造函数之一中调用constexpr
(带有三个参数)。
仅从C ++ 20开始,std::fill_n()
是constexpr
。
您正在编译C ++ 20吗?
memcpy_s()
(我不知道,因为它是Microsoft专用的,我使用Linux)是constexpr
?
(3)据我所知,static
函数中不允许使用constexpr
变量,因此您的id
变量
constexpr static Matrix<N, N, Scalar> id =
在constexpr
identityMatrix()
函数内部
为解决此问题,我建议使用static constexpr
模板变量iMatrix
代替identityMatrix()
函数
template<index_t N, typename Scalar = double>
static constexpr auto iMatrix = []
{
Matrix<N, N, Scalar> m(0);
for (index_t i = 0; i < N; i++)
{
m.at(i, i) = static_cast<Scalar>(1.0);
}
return m;
}();
// ...
constexpr auto id = iMatrix<3>;