我创建了一个简单的4x4矩阵类(列-主)。我希望它是高效的,并使用C++14's的全部能力。我能改进一下吗?
#include <array>
class mat4
{
public:
constexpr mat4() noexcept : matrix() {}
constexpr mat4(const std::array<float, 16> &m) noexcept : matrix(m) {}
constexpr mat4(std::array<float, 16> &&m) noexcept : matrix(std::move(m)) {}
constexpr mat4(const mat4 &other) noexcept : matrix(other.matrix) {}
constexpr mat4(mat4 &&other) noexcept : matrix(std::move(other.matrix)) {}
constexpr bool operator==(const mat4 &other) const noexcept
{
for (size_t i = 0; i < 16; ++i) {
if ((*this)[i] != other[i]) {
return false;
}
}
return true;
}
constexpr bool operator!=(const mat4 &other) const noexcept
{
return !(this->operator==(other));
}
constexpr mat4& operator+=(const mat4 &other) noexcept
{
for (size_t i = 0; i < 16; ++i) {
(*this)[i] += other[i];
}
return *this;
}
constexpr mat4& operator*=(float scalar) noexcept
{
for (size_t i = 0; i < 16; ++i) {
(*this)[i] *= scalar;
}
return *this;
}
mat4& operator=(mat4 other) noexcept
{
std::swap(this->matrix, other.matrix);
return *this;
}
constexpr float& operator[](size_t index) { return const_cast<float&>(static_cast<const std::array<float, 16>&>(matrix)[index]); }
constexpr float operator[](size_t index) const { return matrix[index]; }
void print() const noexcept
{
printf("\n");
printf("[%.2f][%.2f][%.2f][%.2f]\n", matrix[0], matrix[4], matrix[8], matrix[12]);
printf("[%.2f][%.2f][%.2f][%.2f]\n", matrix[1], matrix[5], matrix[9], matrix[13]);
printf("[%.2f][%.2f][%.2f][%.2f]\n", matrix[2], matrix[6], matrix[10], matrix[14]);
printf("[%.2f][%.2f][%.2f][%.2f]\n", matrix[3], matrix[7], matrix[11], matrix[15]);
}
private:
std::array<float, 16> matrix;
};
constexpr const mat4 operator+(mat4 lhs, const mat4 &rhs) noexcept
{
lhs += rhs;
return lhs;
}
constexpr const mat4 operator*(const mat4 &lhs, const mat4 &rhs) noexcept
{
mat4 result;
for (size_t i = 0; i < 4; ++i)
{
for (size_t j = 0; j < 4; ++j)
{
for (size_t k = 0; k < 4; ++k) {
result[i + 4 * j] += lhs[i + 4 * k] * rhs[k + 4 * j];
}
}
}
return result;
}
constexpr const mat4 operator*(mat4 lhs, float scalar) noexcept
{
lhs *= scalar;
return lhs;
}您可以在这个现场演示中测试它。
发布于 2016-08-05 14:28:28
这是试图回答我自己的问题。如果有人能对此发表评论,我将不胜感激--他们同意/不同意我的部分解决办法。
mat4类型的第一个参数应该通过引用传递,而不是通过operator+、operator* (标量版本)和operator=签名中的值传递。在这个参考文献的帮助下,让我们对函数operator+进行分析(就像在本文章中所做的那样),以理解为什么(对operator*和operator=的分析是相似的)。
首先,我们可以在上面的代码中假设两件事:
mat4的构造函数。我在GCC 6.1和Clang 3.8下测试了这一点,在我使用这个类的操作符的所有情况下都是这样。mat4,默认构造函数>=复制构造函数= move构造函数(执行速度越快)。因为std::array是一个聚合,所以移动一个float并不比复制它更快。复制/移动构造函数可能在内部使用类似memcpy和默认构造函数memset (因为mat4's构造函数中的调用matrix() )将所有元素设置为0(我怀疑这会比memcpy慢,甚至可能更快)。在调用operator+时,有两种情况需要考虑:
mat4的第一个参数是lvalue。如果第一个参数是lvalue,则传递值(当前)版本的operator+将调用mat4的复制构造函数将该对象复制到lhs中。然后,当函数返回时,它将调用mat4的move构造函数将lhs移动到operator+返回的对象中。调用移动而不是复制构造函数的原因在上面给出的引用中解释了。以下是有趣的摘录:
(自C++11)在返回语句或抛出表达式中,如果编译器不能执行复制省略,但复制省略的条件满足或将满足,除非源是函数参数,编译器将尝试使用移动构造函数,即使对象是由lvalue指定的;有关详细信息,请参阅返回语句。
但是,如果传递给operator+的参数是rvalue,则将省略将该值复制到lhs中。在这种情况下,只调用移动构造函数。
现在,让我们对operator+的按引用版本进行完全相同的分析,我建议实现如下:
constexpr const mat4 operator+(const mat4 &lhs, const mat4 &rhs) noexcept
{
mat4 result;
for (size_t i = 0; i < 16; ++i) {
result[i] = lhs[i] + rhs[i];
}
return result;
}在这两种情况下,即当函数的第一个参数为lvalue或rvalue时,调用函数只会导致默认构造函数(mat4 result;)的单个调用。注意,临时对象result不会被移动,也不会复制到函数的返回值中,因为复制省略的条件是满足的。
结论:不可否认,最后一种解决方案速度更快。请注意,并不是每次都是这样的,只因为operator+是如何实现的,并且由于两个假设,特别是第二个假设,即默认构造函数>=复制构造函数= move构造函数(注意,如果默认构造函数=复制构造函数=移动构造函数,这个结论也是正确的)。
this->matrix = other.matrix;。只有当类实现了一个资源时,std::swap才会很有趣(参见这里)。constexpr对std::array (C++17)的支持之前,您可以创建自己的数组包装器,这样就不必用operator[]编写那种难看的强制转换了下面是一个受std::array启发的包装器,可以工作:
template<class _Ty, size_t _Size>
struct arr
{
constexpr _Ty& operator[](size_t index) noexcept
{
return _arr[index];
}
constexpr const _Ty& operator[](size_t index) const noexcept
{
return _arr[index];
}
constexpr _Ty *data() noexcept
{
return _arr;
}
constexpr const _Ty *data() const noexcept
{
return _arr;
}
float _arr[_Size];
};https://codereview.stackexchange.com/questions/136541
复制相似问题