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_list
s.
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?
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
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With