I have been looking at how the initializer_list
is implemented so I found section 18.9 of the standard and found a simple enough looking interface. I thought it would be instructive to make my own version which I named MyNamespace::InitializerList
and a use case:
template<class T> class ArrayPrinter { public: ArrayPrinter(MyNamespace::InitializerList<T> list) { for (auto i : list) cout << i << endl; } }; ... ArrayPrinter ap{ {1,2,3} };
I was surprised to find that this did not work and the compiler complained that it couldn't find a suitable constructor (it wanted to give me 3 arguments but section 18.9 only describes a default constructor).
After a bit of fiddling I found that my class had to be named exactly std::initializer_list
in order to work. I could also alias std::initializer_list
it into MyNamespace
but I could not alias MyNamespace::InitializerList
asstd::initializer_list
.
It seems that this it is not really a language feature as it depends on the standard library?
The main point to my question is why the name is so important and what were those 3 arguments it was trying to pass to the constructor?
The name is important because the standard says it is. The standard needs some way for you to be able to say, "this constructor can be passed a braced-init-list containing a sequence of values of the type T". That way was given the name "std::initializer_list
".
You cannot make a class that has all of the language properties of initializer_list
. You can make one that satisfies the conditions of the type specified by section [initializer.list.syn] of the standard. But you'll notice that the only constructor specified there is a default constructor. The only way to create an initializer_list
with actual elements relies on the compiler, not user-land code.
So you can't replicate everything about initializer_list
. Just as you can't replicate std::type_info
. The C++ standard library is not optional.
This answer is not entirely accurate. It is possible to create a fully functional std::initializer_list
- it just needs to meet the specific requirements of the target compiler.
For GCC and clang that requirement is a private ctor. Here is the libc++ implementation (which also happens to work fine with GCC 8.3):
template<class _Ep> class initializer_list { const _Ep* __begin_; size_t __size_; inline constexpr initializer_list(const _Ep* __b, size_t __s) noexcept : __begin_(__b), __size_(__s) {} public: typedef _Ep value_type; typedef const _Ep& reference; typedef const _Ep& const_reference; typedef size_t size_type; typedef const _Ep* iterator; typedef const _Ep* const_iterator; inline constexpr initializer_list() noexcept : __begin_(nullptr), __size_(0) {} inline constexpr size_t size() const noexcept {return __size_;} inline constexpr const _Ep* begin() const noexcept {return __begin_;} inline constexpr const _Ep* end() const noexcept {return __begin_ + __size_;} }; template<class _Ep> inline constexpr const _Ep* begin(initializer_list<_Ep> __il) noexcept { return __il.begin(); } template<class _Ep> inline constexpr const _Ep* end(initializer_list<_Ep> __il) noexcept { return __il.end(); }
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