Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficient and elegant way to initialize vector

Given the following C++14 code:

struct A { /* heavy class, copyable and movable */ };

// complex code to create an A
A f(int);
A g(int);
A h(int);

const std::vector<A> v = { f(1), g(2), h(3) };

I know the A's in the initializer_list are copied into the vector, instead of being moved (there are a lot of questions in stackoverflow about this).

My question is: how can I move them into the vector?

I've only been able to do the ugly IIFE (that keeps v const) and just avoids initializer_list:

const std::vector<A> v = []()
{
    std::vector<A> tmp;
    tmp.reserve(3);
    tmp.push_back( f(1) );
    tmp.push_back( g(2) );
    tmp.push_back( h(3) );
    return tmp;
}();

Is it possible to make this elegant and efficient?

PD: v has to be a std::vector<A> for later use

like image 510
dats Avatar asked Apr 28 '17 09:04

dats


Video Answer


2 Answers

Not sure if it counts as "elegant" but you could use an array (or std::array) which uses aggregate initialization that doesn't suffer from this problem and move_iterator to move the values into the vector.

std::array<A, 3> init = { f(1), g(2), h(3) };

std::vector<A> v{std::make_move_iterator(init.begin()), 
                 std::make_move_iterator(init.end())};

Live demo.

like image 77
Chris Drew Avatar answered Sep 23 '22 03:09

Chris Drew


You ask a C++14 solution, so you can use variadic lambda function with auto arguments.

Following the example of your lambda function...

#include <vector>

struct A
 { };

A f (int) { return {}; } 
A g (int) { return {}; } 
A h (int) { return {}; } 


int main ()
 {
   static const auto getVA = [](auto && ... args)
    {
      using unused = int[];

      std::vector<A> ret;

      ret.reserve(sizeof...(args));

      (void)unused { 0,
            (ret.emplace_back(std::forward<decltype(args)>(args)), 0)... };

      return ret;
    };

   auto va = getVA(f(1), g(2), h(3));
 }

If you prefer old type (not-lambda) function, or you want a solution that works also with C++11, getVA can be written as follows

template <typename ... Args>
std::vector<A> const getVA (Args&& ... args)
 {
   using unused = int[];

   std::vector<A> ret;

   ret.reserve(sizeof...(args));

   (void)unused { 0, (ret.emplace_back(std::forward<Args>(args)), 0)... };

   return ret;
 }
like image 29
max66 Avatar answered Sep 20 '22 03:09

max66