0

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.

  1. I mostly have read vectors over raw pointers. Is there any reason not to change to vectors?
  2. If I change the data to be stored in vectors instead of new[] arrays, do I still need to manually write copy/move assignment and constructor operators for classes? Are there any important differences between vector and new[] move/copy operators?
  3. Suppose I have a class called Levels, which contains several sparse matrix variables. I would like a function to create a vector of Levels, and return it:
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...

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Ben Southworth
  • 291
  • 1
  • 2
  • 12

3 Answers3

2

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 returning 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.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
1
  1. Nothing related to correctness. Just be aware that constructing a vector of size n means it will initialize all of its elements, so you might prefer to construct an empty vector, then reserve(n), then push_back the elements.
  2. No, the implicit move constructor/assignment should take care of it all - unless you suppress them.
  3. Yes, if you don't write code to prevent the move, you'll get an efficient move from std::vector automatically.

Also, consider using an existing library such as Eigen, so you get some fairly optimized routines for free.

Community
  • 1
  • 1
DanielKO
  • 4,422
  • 19
  • 29
  • 3. In most cases, there won't be any move or copy thanks to RVO – galinette Feb 25 '15 at 16:37
  • @galinette Yes, copy elision can happen. I was addressing the question on whether a move can optimize things. RVO is not guaranteed, but a move is. – DanielKO Feb 25 '15 at 16:47
1
  1. No. In 99% of the cases the simplest use of std::vector will do the job better and safer than raw pointers, and in the less common cases where you need to manually manage memory, these class can work with custom allocators/deallocators (for instance, if you want aligned memory for use of aligned SSE intrinsics). If you use custom allocators, the code will be potentially more complex than raw pointers, but more maintainable and less prone to memory problems.

  2. Depending on what your other members are, and what your class does, you may need to implement move/copy assignment/ctors. But this will be much more simple. You may have to implement them yourself, but for your vectors you just need to call the corresponding operators/ctors. The code will be simple, readable, and you will have no risks of segfaults / memory leaks

  3. Yes, but move semantics are not even necessary. Return value optimization will be responsible for the optimized copy (in fact there will be no copy). However this is compiler specific, and not guaranteed by the standard.

galinette
  • 8,896
  • 2
  • 36
  • 87