3

I have a class with a non trivial constructor:

class mystream
{
public:
    mystream(size_t buffersize,size_t index) : buffersize(buffersize),index(index){}

    size_t buffersize;
    size_t index;
};

The mystream instance has an unique id which corresponds with its position in a vector of the managing class:

class mystreammanager
{
public:
   mystreammanager() : streams(8,1024, /* WHAT TO DO HERE ??? */ )
   {
   }

  std::vector<mystream> streams;
};

How can I construct the vector and initialize its elements with an ascending value for the index?

Barry
  • 286,269
  • 29
  • 621
  • 977
Martin Schlott
  • 4,369
  • 3
  • 25
  • 49

5 Answers5

8

A succinct, clear and easy-to-debug way to do this is to defer the construction of the vector to a static class function:

class mystreammanager
{
public:
   mystreammanager() : streams{ generate_streams(1024, 8) }
   {
   }

private:
  static std::vector<mystream> generate_streams(size_t buffersize, size_t qty)
  {
    std::vector<mystream> result;
    result.reserve(qty);
    for(size_t i = 0 ; i < qty ; ++qty) {
      result.emplace_back(buffersize, i); 
    }
    return result;
  } 

  std::vector<mystream> streams;
};

This is optimally efficient because:

  1. RVO causes the vector to be constructed in-place
  2. avoiding list-initialisation means no redundant copies.
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • This is awesome, but not usable if you work in a team as it is also unreadable as an average developer does not expect a method call into the instance itself during initialising (at least, I do not). But thx – Martin Schlott May 20 '15 at 11:14
  • 5
    @MartinSchlott: If your team can't read a straightforward function, then you need a new team. – Mike Seymour May 20 '15 at 11:17
  • @MikeSeymour maybe you are right. But one has to work with what he has :-) . The Problem is IMHO that that situation can escalate when you got more than one vector you have to take special care of in your managing class. I like thinks happening near by. Also it has not really an advantage over a loop AFAIK. – Martin Schlott May 20 '15 at 11:20
  • 2
    @MartinSchlott if you have more than one vector you have to take special care of then I think that is even more reason to defer construction to a separate function. – Chris Drew May 20 '15 at 11:32
  • 1
    The advantage of deferring construction to a function is that RVO gives you optimal performance while the logic is written only once (for example if you have 2 overloaded constructors). – Richard Hodges May 20 '15 at 12:01
  • @RichardHodges you are right. I changed my acceptation. I added a answer where I used your code as base for a template. As the question and your answer draw attention you may add that to your answer if you want. Take also a look at Drax (unfortunatly C++14) – Martin Schlott May 20 '15 at 15:01
  • yes, see my comment there - I avoided std::initializer_list because it puts constraints on the design of your stream object. – Richard Hodges May 20 '15 at 15:36
3

Just use a loop:

mystreammanager() {
    streams.reserve(8);
    for (int i = 0; i < 8; ++i) {
        streams.emplace_back(1024, i);
    }
}
Barry
  • 286,269
  • 29
  • 621
  • 977
3

Compile time version :)

Requires c++14 but could surely be adapted for c++11

#include <cstddef>
#include <vector>
#include <utility>

class mystream
{
public:
  mystream(size_t buffersize,size_t index) : buffersize(buffersize),index(index){}

  size_t buffersize;
  size_t index;
};

template<size_t... Indexes>
std::initializer_list<mystream>    mystream_maker_impl(std::index_sequence<Indexes...>)
{
  return {{1024, Indexes}...};
}

template<size_t N>
std::initializer_list<mystream>    mystream_maker()
{
  return mystream_maker_impl(std::make_index_sequence<N>());
}

class mystreammanager
{
public:
  mystreammanager() : streams(mystream_maker<8>())
  {
  }

  std::vector<mystream> streams;
};
Drax
  • 12,682
  • 7
  • 45
  • 85
  • Awesome. Can you tell me which part is C++14? I cannot accept this answer as I stated C++11 in my question. – Martin Schlott May 20 '15 at 14:20
  • `std::index_sequence` and `std::make_index_sequence` (http://en.cppreference.com/w/cpp/utility/integer_sequence) are C++14, but unless my imagination is playing tricks on me i've seen implementation of those multiple times on stackoverflow. – Drax May 20 '15 at 14:27
  • I tested it on the latest XCode and it only accepted it with compiling with -std=C++14 or -std=gnu++14 not c++11. As I use VS2013 too I do not expect that to compile on all my platforms. Beside that, it would be the solution I was searching for, as near as possible at a compile time solution. – Martin Schlott May 20 '15 at 14:37
  • @MartinSchlott As i said on the previous comment you have to reimplement `std::index_sequence` and `std::make_index_sequence` if you want to compile this on c++11 take a look at http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence :) – Drax May 20 '15 at 14:38
  • unfortunately std::initializer_list is not move-aware, so the `stream` type will need to be copyable. In my view, this is a terrible oversight in the design of `std::initializer_list` – Richard Hodges May 20 '15 at 15:36
2

You can do:

class mystreammanager
{
public:
   mystreammanager() : streams{{1024, 0}, {1024, 1}, {1024, 2}, {1024, 3},
                               {1024, 4}, {1024, 5}, {1024, 6}, {1024, 7}}
   {
   }

  std::vector<mystream> streams;
};

But doing a loop seems safer/simpler.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

I used the answer from @RichardHodges as I was not happy with my first choice. I came up with this template:

template<class T,class ...Args> std::vector<T> generate_with_index(size_t qty,Args ...args)
{
    std::vector<T> result;
    result.reserve(qty);
    for(size_t i = 0 ; i < qty ; ++qty)
        result.emplace_back(i, args...);
    return result;
}

It helps me avoiding redundancy. From a theoretical view I like @Drax solution most as it do the most work during compile time.

Martin Schlott
  • 4,369
  • 3
  • 25
  • 49
  • 1
    in which case you'll want to eliminate redundant copies like this: `template std::vector generate_with_index(size_t qty,Args&& ...args) { std::vector result; result.reserve(qty); for(size_t i = 0 ; i < qty ; ++qty) result.emplace_back(i, std::forward(args)...); return result; }` – Richard Hodges May 20 '15 at 18:31