I made a sparse matrix class for some work I am doing. For the sparse structures, I used pointers, e.g. int* rowInd = new int[numNonZero]
. For the class I wrote copy and move assignment operators and all works fine.
Reading about the move and copy semantics online, I have tangentially found an overwhelming opinion that in modern C++ I should probably not be using raw pointers. If this is the case, then I would like to modify my code to use vectors for good coding practice.
vector<Levels> GetGridLevels(int &n, ... ) { vector<Levels> grids(n); \\ ... Define matrix variables for each Level object in grids ... return grids; }
Will move semantics prevent this from being an expensive copy? I would think so, but it's a vector of objects containing objects containing member vector variables, which seems like a lot...
Yes, use std::vector<T>
instead of raw T *
.
Also yes, the compiler will generate copy and move assignment operators for you and those will very likely have optimal performance, so don't write your own. If you want to be explicit, you can say that you want the generated defaults:
struct S
{
std::vector<int> numbers {};
// I want a default copy constructor
S(const S&) = default;
// I want a default move constructor
S(S &&) noexcept = default;
// I want a default copy-assignment operator
S& operator=(const S&) = default;
// I want a default move-assignment operator
S& operator=(S&&) noexcept = default;
};
Regarding your last question, if I understand correctly, you mean whether return
ing a move-aware type by-value will be efficient. Yes, it will. To get the most out of your compiler's optimizations, follow these rules:
Return by-value (not by const
value, this will inhibit moving).
Don't return std::move(x)
, just return x
(at least if your return type is decltype(x)
) so not to inhibit copy elision.
If you have more than one return
statement, return
the same object on every path to facilitate named return value optimization (NRVO).
std::string
good(const int a)
{
std::string answer {};
if (a % 7 > 3)
answer = "The argument modulo seven is greater than three.";
else
answer = "The argument modulo seven is less than or equal to three.";
return answer;
}
std::string
not_so_good(const int a)
{
std::string answer {"The argument modulo seven is less than or equal to three."};
if (a % 7 > 3)
return "The argument modulo seven is greater than three.";
return answer;
}
For those types where you write move constructors and assignment operators, make sure to declare them noexcept
or some standard library containers (notably std::vector
) will refuse to use them.