Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are the data elements of nested std::initializer_lists guaranteed to be contiguous?

Multi-dimensional initializers can be created by nesting brace-enclosed lists, as in {{1,2,3}, {4,5,6}}. A function accepting this can be written using nested std::initializer_lists.

Are the data elements guaranteed to be contiguous?

Here's an example:

void f(std::initializer_list<std::initializer_list<int>> a)
{
  for(auto const & p: a)
    for(auto const & q: p)
      std::cout << &q << std::endl;
}

int main()
{
  f({{1,2,3}, {4,5,6}});
  return 0;
}

The above code outputs consecutive addresses on my machine.

0x400c60
0x400c64
0x400c68
0x400c6c
0x400c70
0x400c74

It is guaranteed?

Update

The answer must be no.

void g(std::initializer_list<int> a, std::initializer_list<int> b)
{
  f({b,a});
}

int main()
{
  g({1,2,3}, {4,5,6});
  return 0;
}

The output is:

0x400cdc
0x400ce0
0x400ce4
0x400cd0
0x400cd4
0x400cd8
like image 994
AndyJost Avatar asked Jan 04 '14 04:01

AndyJost


2 Answers

C++11 § [support.initlist] 18.9/1 specifies that for std::initializer_list<T> iterator must be T*, so you are guaranteed that sequential elements in a single initializer_list are contiguous.

In the case of nested lists, e.g., std::initializer_list<std::initializer_list<int>>, there is no requirement that all elements are contiguous. The top-level list's begin() must return a pointer to a contiguous array of std::initializer_list<int>, and each of those lists' begin() must return a pointer to a contiguous array of int. Those second-tier int arrays could be scattered all over memory or stored precisely in total order exactly as you observed. Both ways are compliant.

like image 171
Casey Avatar answered Nov 15 '22 06:11

Casey


From here (std::initializer_list) :-

Initializer lists may be implemented as a pair of pointers or pointer and length.

A possible implementation can be (taken & edited from Mingw 4.8.1 initializer_list header ) :

  template<class T>
    class initializer_list
    {
    public:
      typedef T         value_type;
      typedef const T&  reference;
      typedef const T&  const_reference;
      typedef size_t        size_type;
      typedef const T*  iterator;
      typedef const T*  const_iterator;

    private:
      iterator          arr;
      size_type         arr_len;

      // The compiler can call a private constructor.
      constexpr initializer_list(const_iterator it, size_type len)
      : arr(it), arr_len(len) { }

    public:
      constexpr initializer_list() noexcept
      : arr(0), arr_len(0) { }

      // Number of elements.
      constexpr size_type
      size() const noexcept { return arr_len; }

      // First element.
      constexpr const_iterator
      begin() const noexcept { return arr; }

      // One past the last element.
      constexpr const_iterator
      end() const noexcept { return begin() + size(); }
    };

The iterators are not magic, its just playing around with pointers. So the answer is yes, its guaranteed.

In second example of yours, it does show the contiguous memory location, you just flipped a and b

like image 38
P0W Avatar answered Nov 15 '22 07:11

P0W