Std :: array container implementation with C ++ 11 initializer_list

The only and very inconvenient warning of std::array is that it cannot deduce its size from the list of initializers, like built-in C-arrays, the size should be passed as a template.

Is it possible to implement the std :: array-like container (thin shell around the C built-in array) with C++11 initializer_list?

I ask because, unlike std :: array, it automatically infers the size of the array from the list of initializers, which is much more convenient. For example:

 // il_array is the hypothetical container // automatically deduces its size from the initalizer list il_array <int> myarr = {2, 4, 6, 7, 8}; 

We would also like to provide a constructor to indicate the size if a list of initializers was not provided. For example:

 // construct a fixed size array of size 10 il_array <int> myarr2 (10); 

It will also make the container more compatible with other standard containers, for example. vector, deque and list.

As far as I know, this is not possible since a wrapped C-array, for example. T elems [size], must have a constant size, and the member function initialize_list size () is not constant.

In addition, I was wondering if it is possible to implement such a container using a variation template, although from what I read, I do not think that this is possible.

+8
c ++ arrays c ++ 11 initializer-list variadic-templates
source share
4 answers

I think you're out of luck here. The big advantage of std :: array is that it is a POD and can be statically initialized.

If you have a container with a constructor that takes std :: initializer_list, it will have to copy the values ​​(unless it is a permanent reference to the initializer, which is not very useful).

+3
source share

Is it possible to implement the std :: array-like container (thin shell around the C built-in array) with C ++ 0x initializer_list?

Yes , well, while you are ready to cheat. As Mooing Duck points out, no, it doesn't even fool you if compiler developers don't let you . Although, you can still get close enough - you can use initializer lists and a static array that is hidden by the shell.

This is the code I wrote for my personal toolkit. The key is to ignore the size altogether, even for the array, and let the provider container handle it; in this case, initializer_list , which can provide the size via std::distance , thereby avoiding explaining the size of the client side (the term that I just came up with seems to be).

Since this is "someone could have come up with this", this is not a code, but problems with its "return" to the public; in fact, I got the idea from some expert guy whose nickname I don’t remember on the Freenode ##c++ channel, so I think this is a recognition for them:

* EDIT * ed:

 template <typename T> struct carray { // typedefs for iterator. The best seems to be to use std::iterator<std::random_iterator_tag,T,int> here ... template <size_t N> explicit carray (T (&arr)[N]) : ax(arr), sx(N) {} // note the linked article. // This works *only* if the compiler implementor lets you. carray (std::initializer_list<T> X) : ax (X.begin()), sx(std::distance(X.begin(),X.end()) {} // YMMV about the rest of the "rule of N": // no copy constructor needed -- trivial // no destructor needed -- data outscopes the wrapper // no assignment operator needed -- trivial // container functions, like begin(), end(), size()... private: T* ax; size_t const sx; }; 

Using and declaring in C ++ 0x mode is pretty simple (just tested with GCC 4.6 in Fedora 15), but it works with the caveats mentioned in the external links above, so this behavior seems to be undefined:

 using lpp::carray; carray<short const> CS = {1, -7, 4, 188}; 

However, I do not understand why the compiler developer in any case will not implement the initializer_list of integrals as a static array. Your call.

Not only does it work this way, if you can #ifdef the initializer constructor in the path in pre-C ++ 0x mode, you can use it in pre-C ++ 0x; although a preliminary formulation of the data array as its own variable is required, this is IMHO, closest to the original intention (and has the advantage that it can be used and does not cause, for example: problems with the area). (also tested using the above compiler, as well as the Debian Wheezy GCC):

 using lpp::carray; short data[]= {1, -7, 4, 188}; carray<short const> CS (data); 

There! There is no "size" parameter anywhere!

We would also like to provide a constructor to indicate the size if a list of initializers was not provided.

Sorry, this is one of the features that I have not mastered. The problem is how to allocate memory “statically” from an external source, possibly for Allocator. Assuming that this can be done somehow through the allocate helper functor, then the constructor would be something like this:

 explicit carray (size_t N) : ax(allocate(N)), sx(N) {} 

I hope this code helps, as I can see that the question is more or less old.

+2
source share

How about this? I used std::tuple instead of initializer_list because the number of tuple arguments is available at compile time. The tuple_array class below inherits from std::array and adds a template constructor intended for use with std::tuple . The contents of the tuple are copied to the base array using the Assign metaprogram, which simply iterates from N to 0 at compile time. Finally, the make_tuple_array function takes an arbitrary number of parameters and builds a tuple_array . The type of the first argument is considered the type of the array element. Good compilers should exclude an extra copy using RVO. The program works with g ++ 4.4.4 and 4.6.1 with RVO.

 #include <array> #include <tuple> #include <iostream> template <size_t I, typename Array, typename Tuple> struct Assign { static void execute(Array &a, Tuple const & tuple) { a[I] = std::get<I>(tuple); Assign<I-1, Array, Tuple>::execute(a, tuple); } }; template <typename Array, typename Tuple> struct Assign <0, Array, Tuple> { static void execute(Array &a, Tuple const & tuple) { a[0] = std::get<0>(tuple); } }; template <class T, size_t N> class tuple_array : public std::array<T, N> { typedef std::array<T, N> Super; public: template<typename Tuple> tuple_array(Tuple const & tuple) : Super() { Assign<std::tuple_size<Tuple>::value-1, Super, Tuple>::execute(*this, tuple); } }; template <typename... Args> tuple_array<typename std::tuple_element<0, std::tuple<Args...>>::type, sizeof...(Args)> make_tuple_array(Args&&... args) { typedef typename std::tuple_element<0, std::tuple<Args...>>::type ArgType; typedef tuple_array<ArgType, sizeof...(Args)> TupleArray; return TupleArray(std::tuple<Args...>(std::forward<Args>(args)...)); } int main(void) { auto array = make_tuple_array(10, 20, 30, 40, 50); for(size_t i = 0;i < array.size(); ++i) { std::cout << array[i] << " "; } std::cout << std::endl; return 0; } 
+1
source share

I think this question is really very simple. You need a type whose size will be the size of the initializer_list with which it is initialized.

 // il_array is the hypothetical container // automatically deduces its size from the initalizer list il_array <int> myarr = {2, 4, 6, 7, 8}; 

Try the following:

 // il_array is the hypothetical container // automatically deduces its size from the initalizer list std::initalizer_list<int> myarr = {2, 4, 6, 7, 8}; 

Does this do any copying? In the most technical sense ... yes. However, copying a list of initializers does not copy its contents. So it only costs a few copies of the pointer. In addition, any C ++ compiler that is worth using will strip this copy of nothing.

So you have this: an array whose size is known (via std::initializer_list::size ). Limitations are here:

  • size is not available at compile time.
  • array not changed.
  • std::initializer_list pretty bare bones. It does not even have an operator [].

The third is probably the most annoying. But it is also easily fixed:

 template<typename E> class init_array { public: typedef std::initializer_list<E>::value_type value_type; typedef std::initializer_list<E>::reference reference; typedef std::initializer_list<E>::const_reference const_reference; typedef std::initializer_list<E>::size_type size_type; typedef std::initializer_list<E>::iterator iterator; typedef std::initializer_list<E>::const_iterator const_iterator; init_array(const std::initializer_list<E> &init_list) : m_il(init_list) {} init_array() noexcept {} size_t size() const noexcept {return m_il.size();} const E* begin() const noexcept {return m_il.begin();} const E* end() const noexcept {return m_il.end();} const E& operator[](size_type n) {return *(m_il.begin() + n);} private: std::initializer_list m_il; }; 

There; the problem is solved. The initializer list constructor ensures that you can create it directly from the initializer list. And although the copy can no longer be deleted, it still just copies a couple of pointers.

0
source share

All Articles