Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert a vector<T> to initializer_list<T>

Everyone creates std::vector from std::initializer_list, but what about the other way around?

eg. if you use a std::initializer_list as a parameter:

void someThing(std::initializer_list<int> items) { ... } 

There are times when you have your items in a vector<T> instead of a literal list:

std::vector<int> v; // populate v with values someThing(v); // boom! No viable conversion etc. 

The more general question is: how to create an stl::initializer_list from a STL iterable, not just std::vector.

like image 965
Fil Avatar asked Sep 19 '13 13:09

Fil


People also ask

How do you turn a vector into a set?

Get the vector to be converted. Create an empty set, to store the result. Iterate through the vector one by one, and insert each element into the set. Print the resultant set.

What is initializer_list?

An object of type std::initializer_list<T> is a lightweight proxy object that provides access to an array of objects of type const T .

Is initializer list an array?

initializer list: The values in the list are taken as initializers for the elements in the array . If the list has fewer initializers than elements in the array , the remaining elements are value-initialized (i.e., for class types, the default constructor is called; for fundamental types, they are zero-initialized).


2 Answers

The answer is NO, you cannot do that.

An object of type std::initializer_list<T> is a lightweight proxy object that provides access to an array of objects of type T. A std::initializer_list object is automatically constructed when:

  • a braced-init-list is used in list-initialization, including function-call list initialization and assignment expressions (not to be confused with constructor initializer lists)
  • a braced-init-list is bound to auto, including in a ranged for loop

As far as library support goes, std::initializer_list only has a default constructor that constructs an empty list, and its iterators are constant. The lack of a push_back() member means you cannot apply e.g. a std::copy with a std::back_inserter iterator adaptor to fill it, and neither can you assign through such iterators directly:

#include <algorithm> #include <initializer_list> #include <iterator> #include <vector>  int main()  {     auto v = std::vector<int> { 1, 2 };     std::initializer_list<int> i;     auto it = std::begin(i);     *it = begin(v); // error: read-only variable is not assignable } 

Live Example

If you look at the Standard Containers, in addition to accepting std::initializer_list in their constructors / inserters, they all have constructors / inserters taking an iterator pair, and the implementation is likely to delegate the initializer_list function to the corresponding iterator pair function. E.g. the std::vector<T>::insert function in libc++ is this simple one-liner:

 iterator insert(const_iterator __position, initializer_list<value_type> __il)         {return insert(__position, __il.begin(), __il.end());} 

You should modify your code along similar lines:

void someThing(std::initializer_list<int> items) {     someThing(items.begin(), items.end()); // delegate }  template<class It> void someThing(It first, It last) {     for (auto it = first, it != last; ++it) // do your thing } 

In times when you have your items in a vector instead of a literal list:

std::vector<int> v = { 1, 2 }; auto i = { 1, 2 }; someThing(begin(v), end(v)); // OK someThing(i); // also OK someThing({1, 2}); // even better 
like image 122
TemplateRex Avatar answered Oct 11 '22 12:10

TemplateRex


Apparently no, it is not possible. There is no such constructor (and I believe for good reasons), std::initializer_list is a weird creature.

What you could do instead is to change someThing() to accept a pair of iterators. In that way you get what you want, provided you can change the signature of that function (it isn't in a third party library, etc).

like image 20
Ali Avatar answered Oct 11 '22 12:10

Ali