0

I mean to build a vector of M N-sized-vectors, i.e., with K=M*N total elements, from a K-sized vector with a linear arrangement of the same set of values. Moreover, I would do that in a class constructor, although I guess that is irrelevant. My code is shown below.

What should I use in the pushback line?

template <int dim>
class vec2d {
  public:
    // Constructor - Version 1: 2D array as initializer
    vec2d(const std::vector<std::vector<double> >& c);
    // Constructor - Version 2: 1D array as initializer
    vec2d(const std::vector<double>& c);
    ...
  protected:
    std::vector<std::vector<double> > _vec2d;
};

// Version 1: 2D array as initializer
...

// Version 2: 1D array as initializer
template <int dim>
vec2d::vec2d(const std::vector<double>& c)
{
    for (size_t i = 0; i < (c.size() / dim); i++)
        _vec2d.push_back(std::vector<double>(c(i * dim), c((i+1) * dim - 1)));  // <- Fix this line
}
  • 6
    Personally I would subvert the problem. Make `vec2d`'s storage a 1d vector, and then overload `operator()` to take two indies to return the correct element in the vector. This makes construction easy, and gives you better performance. With a 2d vector, each row can be somewhere else in memory, which can really hamper its cache efficiency. – NathanOliver Mar 22 '21 at 16:23
  • I don't know if it is out of scope, I don't like two vectors nested. It subtly says that the inner vector can be of any size. But really, it cannot. So I would move to a template class that makes use of std::array. In that way, you can force the "matrix" to be squared. And probably you get more performance by increasing the locality. – Stefano Buora Mar 22 '21 at 16:27
  • You must construct your vector, you cannot just assign it as a rvalue. So instead of push_back, you must use emplace_back – TUI lover Mar 22 '21 at 16:29
  • If you have to keep the code, I would create the std::vector on one line (so you can spot errors on vector creation) then I would invoke v.push_back( std::move( tmp_vec_double ) ); – Stefano Buora Mar 22 '21 at 16:30
  • @NathanOliver - I agree 100%... but I am inheriting from a class of inherited code. I cannot change that right now. I would very much appreciate knowing if what I mean to do is possible, and if so, how. – sancho.s ReinstateMonicaCellio Mar 22 '21 at 16:35
  • @StefanoBuora - I know I can do that. But I would very much appreciate knowing if what I mean to do is possible, and if so, how. That is why I emphasized in the title "in a concise way". – sancho.s ReinstateMonicaCellio Mar 22 '21 at 16:37
  • Can you assume that `c` has a multiple of `dim` elements? – Caleth Mar 22 '21 at 17:31
  • @Caleth - Yes, that is checked separately. I meant to provide a no-distraction snippet. – sancho.s ReinstateMonicaCellio Mar 23 '21 at 02:31

2 Answers2

1

std::vector has a constructor that takes an iterator range. Using that you can change you loop to use that like

template <int dim>
vec2d::vec2d(const std::vector<double>& c)
{
    auto begin = c.begin();
    auto end = begin + dim;
    for (size_t i = 0; i < (c.size() / dim); i++) {
        _vec2d.emplace_back(begin, end);
        begin += dim; // move range to next dim sized block
        end += dim;   // this could be moved into the for loops increment section
    }
}
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
1

what about:

template <int dim>
vec2d::vec2d(const std::vector<double>& c)
{
    for (auto i = c.begin(); i < c.end(); i+=dim )
        _vec2d.push_back({i, i+dim});
}

or mixing with the hint from @NathanOlivier:

template <int dim>
vec2d::vec2d(const std::vector<double>& c)
{
    for (auto i = c.begin(); i < c.end(); i+=dim )
        _vec2d.emplace_back(i, i+dim);
}

we may debate if "emplace" is longer than "push" + "{}", but it should avoid a call to the move operator on the temporary vector passed as push_back() argument.

As pointed out, it assumes, based on your example that:

  • the c size is multiple of dim (or equal to dim*dim)
  • the _vec2d has been cleared before

As optimization you should add _vec2d.resize(c.size()/dim) or _vec2d.resize(dim) before performing the series of invocation of the push_back() method.

Stefano Buora
  • 1,052
  • 6
  • 12