Implementation of std::initializer_list
Asked Answered
M

3

30

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?

Margit answered 10/8, 2013 at 17:7 Comment(10)
The std::initializer_list class is very much compiler specific.Marshy
Just because something is in the standard library doesn't mean it's not built into the language whole. C++ in its entirely contains both the language core and the standard library.Marquise
Is new operator a language feature? It depends on std::bad_alloc. What about dynamic_cast, which depends on std::bad_cast? typeid operator producing an object of type std::type_info? There's a whole section in the standard called "Language support library".Anacreon
After adding the missing template argument to ArrayPrinter<int> ap{ {1,2,3} }; it works as expected: live exampleLightweight
template <typename... TT> InitializerList(TT&&... pp) : storage{pp...} {} is just forwarding thestd::initializer_list to the vector.Margit
what compiler are you using?Soldier
clang++ 3.2 but given the comments above I don't think that mattersMargit
@Margit No std::initializer_list is created in my example. The {1,2,3} which appears there (is a braced-init-list and) is used to construct a MyNamespace::InitializerList<int> by calling the template ctor in the same way as in ArrayPrinter<int> ap( MyNamespace::InitializerList<int>(1,2,3) );Lightweight
This blog post is my experience implementing std::initializer_list. fauxfoefriend.blogspot.com/2013/05/uniform-initialization.htmlAgler
Why did you think this would work as a real initializer_list? How is a compiler meant to guess what you want it to do with an arbitrary type you create? More to the point, where is the definition of that type? This seems very incomplete. Also, the Coliru link claiming to make this "work as expected" doesn't really make anything work, since it just implements the user-defined faux-initialisation-list by using a vector, which is a waste of dynamic allocation and only works because it has a ctor taking the real initializer_list anyway...Broadbent
E
27

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.

Eloiseloisa answered 10/8, 2013 at 21:11 Comment(4)
@kuroineko What does Arduino lack that the Standard requires of a compliant freestanding implementation?Broadbent
@underscore_d: Well, <initializer_list> is on the list of headers that are required for freestanding implementations. I don't use Arduino, but if they don't provide that, then they're not providing even a freestanding C++11 implementation.Eloiseloisa
A quick search indicates that indeed they (i.e. avr-gcc) don't. It would've been clearer and not taken any more space to just say that in the original comment! Unfortunate to learn, in any case.Broadbent
Sorry for my cheeky remark, I just happened to stumble on that problem and didn't elaborate on it.Microbiology
D
3

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();
}
Dumpy answered 10/7, 2020 at 9:54 Comment(1)
Works on Arduino AVR Core 1.8.5, but you need to wrap the code with namespace std.Imitate
C
2

I've found this post when I had issues implementing initializer_list myself and found it very useful to write my own version that would compile on msvc, gcc, avr-gcc and clang. This question may be old but I would like to share my solution in hope that others find it useful:

#include <stddef.h>

namespace std
{
#if defined(__GNUC__)
// Copyright (C) 2008-2020 Free Software Foundation, Inc.
// Copyright (C) 2020 Daniel Rossinsky <[email protected]>
//
// This file is part of GCC.
//
// GCC is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3, or (at your option)
// any later version.
//
// GCC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.

// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.

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

    private:
        iterator  m_array;
        size_type m_len;

        // The compiler can call a private constructor.
        constexpr initializer_list(const_iterator itr, size_type st)
            : m_array(itr), m_len(st) { }

    public:
        constexpr initializer_list() noexcept : m_array(0), m_len(0) { }

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

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

        // One past the last element.
        constexpr const_iterator end() const noexcept { return begin() + size(); }
    };
#elif defined(__clang__)
// Copyright (c) 2019 Chandler Carruth <https://github.com/chandlerc>
// Copyright (c) 2018 Louis Dionne <https://github.com/ldionne>
// Copyright (c) 2017 Eric <https://github.com/EricWF>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ---- LLVM Exceptions to the Apache 2.0 License ----
//
// As an exception, if, as a result of your compiling your source code, portions
// of this Software are embedded into an Object form of such source code, you
// may redistribute such embedded portions in such Object form without complying
// with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
//
// In addition, if you combine or link compiled forms of this Software with
// software that is licensed under the GPLv2 ("Combined Software") and if a
// court of competent jurisdiction determines that the patent provision (Section
// 3), the indemnity provision (Section 9) or other Section of the License
// conflicts with the conditions of the GPLv2, you may retroactively and
// prospectively choose to deem waived or otherwise exclude such Section(s) of
// the License, but only in their entirety and only with respect to the Combined
// Software.

    template<typename T>
    class initializer_list
    {
    private:
        const T* m_first;
        const T* m_last;

    public:
        using value_type      = T;
        using reference       = const T&;
        using const_reference = const T&;
        using size_type       = size_t;
        using iterator        = const T*;
        using const_iterator  = const T*;

        initializer_list() noexcept : m_first(nullptr), m_last(nullptr) {}

        // Number of elements.
        size_t size() const noexcept { return m_last - m_first; }

        // First element.
        const T* begin() const noexcept { return m_first; }

        // One past the last element.
        const T* end() const noexcept { return m_last; }
    };
#elif defined(_MSC_VER)
// Copyright (c) Microsoft Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ---- LLVM Exceptions to the Apache 2.0 License ----
//
// As an exception, if, as a result of your compiling your source code, portions
// of this Software are embedded into an Object form of such source code, you
// may redistribute such embedded portions in such Object form without complying
// with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
//
// In addition, if you combine or link compiled forms of this Software with
// software that is licensed under the GPLv2 ("Combined Software") and if a
// court of competent jurisdiction determines that the patent provision (Section
// 3), the indemnity provision (Section 9) or other Section of the License
// conflicts with the conditions of the GPLv2, you may retroactively and
// prospectively choose to deem waived or otherwise exclude such Section(s) of
// the License, but only in their entirety and only with respect to the Combined
// Software.

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

        constexpr initializer_list() noexcept : m_first(nullptr), m_last(nullptr) {}

        constexpr initializer_list(const T* first, const T* last) noexcept
            : m_first(first), m_last(last) {}

        // First element.
        constexpr const T* begin() const noexcept { return m_first; }

        // One past the last element.
        constexpr const T* end() const noexcept { return m_last; }

        // Number of elements.
        constexpr size_t size() const noexcept
        {
            return static_cast<size_t>(m_last - m_first);
        }

    private:
        const T* m_first;
        const T* m_last;
    };
#else
    #error "Initializer_list is not supported for this compiler"
#endif

    template<typename T>
    constexpr const T* begin(initializer_list<T> il) noexcept 
    {
        return il.begin();
    }

    template<typename T>
    constexpr const T* end(initializer_list<T> il) noexcept
    {
        return il.end();
    }
}

NOTE:

I added copyright notices with license preambles of each implementation directly above the implementation in order to give credit to whom credit is due for the code and promote fair use.

IANAL:

The only copyright that was included properly was from gcc, the msvc version lacks a year and clang (llvm) should have copyright assigned to the contributors which I did manually. However, Im not an expert and may have missed/misunderstood the way I am allowed to share the code. If so, please let me know and I will apply changes immediately.

Also, as far as I understand Apache-2.0 is compatible with GPLv3 in only one direction. you can sublicense my entire code under GPLv3 but not under Apache-2.0

PROOF OF WORK:

The example I used to show proof my version works is taken from learncpp:

  1. Arduino 1.8.9
  2. x86 msvc v19.28
  3. x86-64 clang (trunk)
  4. x86-64 gcc (trunk)
Coppice answered 14/1, 2021 at 13:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.