- Added move constructors and move assignment operators to `math_VectorBase`, `math_Matrix`, and `math_DoubleTab`
- Optimized move operations to avoid unnecessary copying when dimensions match and both objects use heap allocation
- Added comprehensive test coverage for move semantics with both heap-allocated (large) and buffer-allocated (small) objects
EXPECT_NEAR(aMatrixACopy(2, 1), aExpectedAB(2, 1), Precision::Confusion());
EXPECT_NEAR(aMatrixACopy(2, 2), aExpectedAB(2, 2), Precision::Confusion());
}
+
+// Tests for Move Semantics
+TEST(MathMatrixTest, MoveSemantics)
+{
+ // --- Move Constructor ---
+
+ // Large matrix (heap allocated)
+ Standard_Integer aRows = 10;
+ Standard_Integer aCols = 10;
+ math_Matrix aMat1(1, aRows, 1, aCols);
+ aMat1.Init(1.0);
+ aMat1(1, 1) = 2.0;
+
+ // Move aMat1 to aMat2
+ math_Matrix aMat2(std::move(aMat1));
+
+ EXPECT_EQ(aMat2.RowNumber(), aRows);
+ EXPECT_EQ(aMat2.ColNumber(), aCols);
+ EXPECT_EQ(aMat2(1, 1), 2.0);
+
+ // Verify source state (should be empty after move)
+ EXPECT_EQ(aMat1.RowNumber(), 0);
+
+ // Small matrix (buffer allocated)
+ Standard_Integer aSmallRows = 4;
+ Standard_Integer aSmallCols = 4;
+ math_Matrix aSmallMat1(1, aSmallRows, 1, aSmallCols);
+ aSmallMat1.Init(1.0);
+
+ // Move aSmallMat1 to aSmallMat2 (should copy because of buffer)
+ math_Matrix aSmallMat2(std::move(aSmallMat1));
+
+ EXPECT_EQ(aSmallMat2.RowNumber(), aSmallRows);
+ EXPECT_EQ(aSmallMat2(1, 1), 1.0);
+
+ // Source remains valid for buffer-based matrix
+ EXPECT_EQ(aSmallMat1.RowNumber(), aSmallRows);
+ EXPECT_EQ(aSmallMat1(1, 1), 1.0);
+
+ // --- Move Assignment ---
+
+ // Large matrix move assignment
+ math_Matrix aMatAssign1(1, aRows, 1, aCols);
+ aMatAssign1.Init(5.0);
+
+ math_Matrix aMatAssign2(1, aRows, 1, aCols);
+ aMatAssign2.Init(0.0);
+
+ aMatAssign2 = std::move(aMatAssign1);
+
+ EXPECT_EQ(aMatAssign2(1, 1), 5.0);
+ EXPECT_EQ(aMatAssign1.RowNumber(), 0);
+}
EXPECT_EQ(aNegVec.Upper(), 1);
EXPECT_EQ(aNegVec.Max(), 1);
EXPECT_EQ(aNegVec.Min(), -2);
-}
\ No newline at end of file
+}
+
+// Tests for Move Semantics
+TEST(MathVectorTest, MoveSemantics)
+{
+ // --- Move Constructor ---
+
+ // Large vector (heap allocated)
+ Standard_Integer aLen = 100;
+ math_Vector aVec1(1, aLen);
+ for (Standard_Integer i = 1; i <= aLen; ++i)
+ {
+ aVec1(i) = static_cast<Standard_Real>(i);
+ }
+
+ // Move aVec1 to aVec2
+ math_Vector aVec2(std::move(aVec1));
+
+ EXPECT_EQ(aVec2.Length(), aLen);
+ EXPECT_EQ(aVec2(1), 1.0);
+ EXPECT_EQ(aVec2(aLen), static_cast<Standard_Real>(aLen));
+
+ // Verify source state (length should be 0 after move for NCollection_Array1)
+ // Note: calling Length() is safe as it just returns size.
+ EXPECT_EQ(aVec1.Length(), 0);
+
+ // Small vector (buffer allocated)
+ Standard_Integer aSmallLen = 10;
+ math_Vector aSmallVec1(1, aSmallLen);
+ for (Standard_Integer i = 1; i <= aSmallLen; ++i)
+ {
+ aSmallVec1(i) = static_cast<Standard_Real>(i);
+ }
+
+ // Move aSmallVec1 to aSmallVec2 (should copy because of buffer)
+ math_Vector aSmallVec2(std::move(aSmallVec1));
+
+ EXPECT_EQ(aSmallVec2.Length(), aSmallLen);
+ EXPECT_EQ(aSmallVec2(1), 1.0);
+
+ // Source remains valid for buffer-based vector
+ EXPECT_EQ(aSmallVec1.Length(), aSmallLen);
+ EXPECT_EQ(aSmallVec1(1), 1.0);
+
+ // --- Move Assignment ---
+
+ // Large vector move assignment
+ math_Vector aVecAssign1(1, aLen);
+ for (Standard_Integer i = 1; i <= aLen; ++i)
+ {
+ aVecAssign1(i) = static_cast<Standard_Real>(i);
+ }
+
+ math_Vector aVecAssign2(1, aLen);
+ aVecAssign2.Init(0.0);
+
+ aVecAssign2 = std::move(aVecAssign1);
+
+ EXPECT_EQ(aVecAssign2.Length(), aLen);
+ EXPECT_EQ(aVecAssign2(1), 1.0);
+
+ EXPECT_EQ(aVecAssign1.Length(), 0);
+}
#include <Standard_Boolean.hxx>
#include <array>
+#include <utility>
class math_DoubleTab
{
{
}
+ //! Move constructor
+ math_DoubleTab(math_DoubleTab&& theOther) noexcept
+ : myBuffer{},
+ myArray(theOther.myArray.IsDeletable()
+ ? std::move(theOther.myArray)
+ : (theOther.NbRows() * theOther.NbColumns() <= THE_BUFFER_SIZE
+ ? NCollection_Array2<Standard_Real>(*myBuffer.data(),
+ theOther.LowerRow(),
+ theOther.UpperRow(),
+ theOther.LowerCol(),
+ theOther.UpperCol())
+ : NCollection_Array2<Standard_Real>(theOther.LowerRow(),
+ theOther.UpperRow(),
+ theOther.LowerCol(),
+ theOther.UpperCol())))
+ {
+ if (!theOther.myArray.IsEmpty())
+ {
+ myArray.Assign(theOther.myArray);
+ }
+ }
+
//! Copy data to theOther
void Copy(math_DoubleTab& theOther) const { theOther.myArray.Assign(myArray); }
+ //! Returns true if the internal array is deletable (heap-allocated)
+ Standard_Boolean IsDeletable() const { return myArray.IsDeletable(); }
+
//! Set lower row index
void SetLowerRow(const Standard_Integer theLowerRow) { myArray.UpdateLowerRow(theLowerRow); }
return Value(theRowIndex, theColIndex);
}
+ //! Assignment operator
+ math_DoubleTab& operator=(const math_DoubleTab& theOther)
+ {
+ if (this != &theOther)
+ {
+ myArray = theOther.myArray;
+ }
+ return *this;
+ }
+
+ //! Move assignment operator
+ math_DoubleTab& operator=(math_DoubleTab&& theOther) noexcept
+ {
+ if (this == &theOther)
+ {
+ return *this;
+ }
+
+ if (myArray.IsDeletable() && theOther.myArray.IsDeletable()
+ && myArray.NbRows() == theOther.myArray.NbRows()
+ && myArray.NbColumns() == theOther.myArray.NbColumns()
+ && myArray.LowerRow() == theOther.myArray.LowerRow()
+ && myArray.LowerCol() == theOther.myArray.LowerCol())
+ {
+ myArray.Move(theOther.myArray);
+ }
+ else
+ {
+ myArray = theOther.myArray;
+ }
+ return *this;
+ }
+
//! Destructor
~math_DoubleTab() = default;
math_Matrix& operator=(const math_Matrix& Other) { return Initialized(Other); }
//! Move assignment operator
- inline math_Matrix& operator=(math_Matrix&& Other) noexcept;
+ inline math_Matrix& operator=(math_Matrix&& Other);
//! Returns the product of 2 matrices.
//! An exception is raised if the dimensions are different.
//==================================================================================================
-inline math_Matrix& math_Matrix::operator=(math_Matrix&& Other) noexcept
+inline math_Matrix& math_Matrix::operator=(math_Matrix&& Other)
{
- if (this != &Other)
+ if (this == &Other)
+ {
+ return *this;
+ }
+
+ if (Array.IsDeletable() && Other.Array.IsDeletable() && RowNumber() == Other.RowNumber()
+ && ColNumber() == Other.ColNumber() && LowerRow() == Other.LowerRow()
+ && LowerCol() == Other.LowerCol())
{
Array = std::move(Other.Array);
}
+ else
+ {
+ Initialized(Other);
+ }
return *this;
}
#include <math_Matrix.hxx>
#include <array>
+#include <utility>
//! This class implements the real vector abstract data type.
//! Vectors can have an arbitrary range which must be defined at
void Init(const TheItemType theInitialValue);
//! Constructs a copy for initialization.
- //! An exception is raised if the lengths of the vectors are different.
inline math_VectorBase(const math_VectorBase& theOther);
+ //! Move constructor
+ inline math_VectorBase(math_VectorBase&& theOther) noexcept;
+
//! Returns the length of a vector
inline Standard_Integer Length() const { return Array.Length(); }
math_VectorBase& operator=(const math_VectorBase& theOther) { return Initialized(theOther); }
+ //! Move assignment operator
+ inline math_VectorBase& operator=(math_VectorBase&& theOther);
+
//! returns the inner product of 2 vectors.
//! An exception is raised if the lengths are not equal.
Standard_NODISCARD inline TheItemType Multiplied(const math_VectorBase& theRight) const;
{
}
+template <typename TheItemType>
+math_VectorBase<TheItemType>::math_VectorBase(math_VectorBase<TheItemType>&& theOther) noexcept
+ : myBuffer{},
+ Array(theOther.Array.IsDeletable()
+ ? std::move(theOther.Array)
+ : (theOther.Length() <= math_VectorBase::THE_BUFFER_SIZE
+ ? NCollection_Array1<TheItemType>(*myBuffer.data(),
+ theOther.Lower(),
+ theOther.Upper())
+ : NCollection_Array1<TheItemType>(theOther.Lower(), theOther.Upper())))
+{
+ if (!theOther.Array.IsEmpty())
+ {
+ Array.Assign(theOther.Array);
+ }
+}
+
template <typename TheItemType>
void math_VectorBase<TheItemType>::SetLower(const Standard_Integer theLower)
{
return *this;
}
+template <typename TheItemType>
+math_VectorBase<TheItemType>& math_VectorBase<TheItemType>::operator=(
+ math_VectorBase<TheItemType>&& theOther)
+{
+ if (this == &theOther)
+ {
+ return *this;
+ }
+
+ if (Array.IsDeletable() && theOther.Array.IsDeletable() && Lower() == theOther.Lower()
+ && Length() == theOther.Length())
+ {
+ Array.Move(theOther.Array);
+ }
+ else
+ {
+ Initialized(theOther);
+ }
+ return *this;
+}
+
template <typename TheItemType>
void math_VectorBase<TheItemType>::Dump(Standard_OStream& theO) const
{