Implementing a std::array-like container with a C++11 initializer_list Implementing a std::array-like container with a C++11 initializer_list arrays arrays

Implementing a std::array-like container with a C++11 initializer_list


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

If you have a container with a constructor taking a std::initializer_list, it would have to copy the values (unless it is just a constant reference to the initializer, which isn't very useful).


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

Yes, well, so long as you are willing to cheat. As Mooing Duck points out, no, not even cheating, unless the compiler implementors let you. Though, it is still possible to get close enough -- it is possible to use initializer lists and a static array that is hidden by the wrapper.

This is some code I wrote for my personal toolbox. The key is to disregard the size altogether, even for the array, and let the provider container handle it; in this case, initializer_list who can provide the size via std::distance, thus avoiding client-side size explicitation (a term that I just invented, it seems).

Since it is the "anyone could've come up with it" kind of code, no problems about providing it "back" to the public; in fact, I got the idea from some expert guy whose nick I don't remember at Freenode's ##c++ channel, so I guess the recognition is 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;};

Usage and declaration in C++0x mode is pretty simple (just tested with GCC 4.6 in Fedora 15), but it works with the caveats noted in the external links above, so it is apparently undefined behaviour:

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

However, I don't see why a compiler implementor would not implement an initializer_list of integrals as a static array anyway. Your call.

Not only it works like that, provided you can #ifdef the initializer constructor out of the way in pre-C++0x mode, you can actually use this in pre-C++0x; although predeclaration of the data array as its own variable will be needed, it is IMHO the closest it gets to the original intent (and it has the advantage of being usable and not causing eg.: scope issues). (also tested with the above compiler, plus Debian Wheezy's GCC):

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

There! No "size" parameter anywhere!

We would also want to provide a constructor to specify the size if an initializer list was not provided.

Sorry, this is one feature I have not maganed to implement. The problem is how to assign the memory "statically" from an outside source, perhaps an Allocator. Assuming it could be done somehow via a helper functor allocate, then the constructor would be something like this:

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

I hope this code is of help, as I see the question is more or less old.


How about this one? I used std::tuple instead of an initializer_list because the number of tuple arguments are available at compile-time. The tuple_array class below inherits from std::array and adds a templated constructor that is meant to be used with a std::tuple. The contents of the tuple are copied to the underlying array storage using a meta-program Assign, which simply iterates from N down to 0 at compile-time. Finally, the make_tuple_array function accepts arbitrary number of parameters and constructs a tuple_array. The type of the first argument is assumed to be the element type of the array. Good compilers should eliminate the extra copy using RVO. The program works on 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;}