Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to emplace elements while constructing std::vector?

I want to construct an std::vector with some elements having these elements constructed by some particular constructor rather than the default constructor. In other words I want to emplace the elements while constructing the vector. How can I do that?

Consider this:

struct Item
{
    Item(double) {}
    Item(const Item&) = delete;
    Item(Item&&) = delete;
};
std::vector<Item> vv(10, 3.14); // Fails! Tries to copy while I want to emplace.
like image 606
Vahagn Avatar asked Jan 04 '23 08:01

Vahagn


2 Answers

You can use the vector constructor that takes two iterators. For example you could use iterators from a vector<double>.

std::vector<double> init(10, 3.14);
std::vector<Item> vv(init.begin(), init.end());

Live demo.

Or you could write your own custom iterator class to do the initialization:

struct ItemInitializer {
  int index;
  double value;

  using value_type = double;
  using difference_type = int;  
  using pointer = const double*;
  using reference = const double&;
  using iterator_category = std::forward_iterator_tag;

  ItemInitializer() : index(0), value(0.0) {}
  ItemInitializer(int index, double v = 0.0) : index(index), value(v) {}

  bool             operator!=(const ItemInitializer& other) const { return other.index != index; }
  ItemInitializer& operator++() { index++; return *this; }
  ItemInitializer  operator++(int) { ItemInitializer ret(index, value); index++; return ret; }
  const double&    operator*() const { return value; }  
  const double*    operator->() const { return &value; } 
};

std::vector<Item> vv(ItemInitializer{0, 3.14}, ItemInitializer{10});

Live demo.

like image 121
Chris Drew Avatar answered Jan 20 '23 16:01

Chris Drew


Your Item class doesn't support copies nor moves. This will prevent most operations on std::vector from compiling, including std::vector::reserve and std::vector::resize. If you really have such a class, you might want an std::vector<std::aligned_storage_t<sizeof(Item), alignof(Item)>> instead.

If you can add a move constructor to Item, you can create your helper function instead (as the constructor overload that you're using is defined in terms of copying). Note that the version below only works for unary constructors.

template <typename T, typename Arg>
auto make_vector(std::size_t n, Arg&& arg)
{
    std::vector<T> result;
    result.reserve(n);

    for(std::size_t i = 0; i < n; ++i)
        result.emplace_back(arg);

    return result;
}

Usage:

auto vec = make_vector<Item>(10, 3.14);
like image 27
Vittorio Romeo Avatar answered Jan 20 '23 16:01

Vittorio Romeo