std::initializer_list的实现

28

我一直在研究initializer_list的实现方式,于是我找到了标准的18.9节,并找到了一个看起来足够简单的接口。我认为制作自己的版本并将其命名为MyNamespace::InitializerList并提供一个使用案例将是很有启发性的:

template<class T>
class ArrayPrinter
{
public:
    ArrayPrinter(MyNamespace::InitializerList<T> list)
    {
        for (auto i : list) cout << i << endl;
    }
};

...

ArrayPrinter ap{ {1,2,3} };

我惊讶地发现这并不起作用,编译器抱怨它找不到合适的构造函数(它想给我3个参数,但第18.9节只描述了一个默认构造函数)。

经过一番尝试,我发现我的类必须被命名为std :: initializer_list才能工作。我也可以将std :: initializer_list别名为MyNamespace,但我无法将MyNamespace :: InitializerList别名为std :: initializer_list

看起来这不是真正的语言特性,因为它依赖于标准库

我的问题的主要点是为什么名称如此重要,那些3个参数是尝试传递给构造函数的?


2
仅仅因为某些东西在标准库中并不意味着它不是整个语言的一部分。C++完全包含了语言核心和标准库。 - GManNickG
6
"new"运算符是一种语言特性吗?它取决于"std::bad_alloc"。那么"dynamic_cast"呢,它取决于"std::bad_cast"吗?"typeid"运算符生成一个"typeid_info"类型的对象吗?标准中有一个完整的章节称为"语言支持库"。 - Igor Tandetnik
1
ArrayPrinter<int> ap{ {1,2,3} };中添加了缺失的模板参数后,它按预期工作:实时示例 - dyp
1
@DrYap 在我的例子中没有创建std::initializer_list。出现的 {1,2,3}(是一个花括号初始化列表)用于通过调用模板构造函数来构造MyNamespace::InitializerList<int>,就像在 ArrayPrinter<int> ap( MyNamespace::InitializerList<int>(1,2,3) ); 中一样。 - dyp
2
这篇博客是我实现std :: initializer_list的经验。http://fauxfoefriend.blogspot.com/2013/05/uniform-initialization.html - Graznarak
显示剩余5条评论
3个回答

26
重要的是名称,因为标准规定了它。标准需要一些方法让您能够说,“此构造函数可以传递一个包含类型T值序列的花括号初始化列表”。这种方法被命名为“std::initializer_list”。
您无法创建具有initializer_list的所有语言属性的类。您可以创建一个满足标准中[initializer.list.syn]指定类型条件的类。但是您会注意到,那里指定的唯一构造函数是默认构造函数。创建具有实际元素的initializer_list的唯一方法依赖于编译器,而不是用户代码。
因此,您不能复制有关initializer_list的所有内容。就像您无法复制std::type_info一样。 C++标准库不是可选的

@kuroineko,Arduino缺少哪些标准规定的符合自由立式实现所需的要求? - underscore_d
@underscore_d:嗯,<initializer_list>在需要自由实现的头文件列表中。我不使用Arduino,但如果他们没有提供它,那么他们甚至没有提供一个自由实现的C++11。 - Nicol Bolas
快速搜索表明他们(即avr-gcc)确实没有。在原始评论中直接这样说会更清楚,也不会占用更多的空间!无论如何,这是一个不幸的发现。 - underscore_d
抱歉我说话有些无礼,我只是偶然遇到了那个问题,没有详细说明。 - kuroi neko

2
这个答案并不完全准确。创建一个完全功能的std::initializer_list是有可能的,只需要满足目标编译器的特定要求即可。
对于GCC和clang,这个要求是一个私有构造函数。以下是libc++的实现(也适用于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();
}

适用于Arduino AVR Core 1.8.5,但需要使用namespace std将代码包装起来。 - Benoit Blanchon

1

当我自己实现initializer_list时遇到问题,我发现了这篇文章,并且发现它非常有用,可以编写适用于msvc、gcc、avr-gcc和clang的版本。 虽然这个问题可能很旧,但我想分享我的解决方案,希望其他人也会觉得有用:

#include <stddef.h>

namespace std
{
#if defined(__GNUC__)
// Copyright (C) 2008-2020 Free Software Foundation, Inc.
// Copyright (C) 2020 Daniel Rossinsky <danielrossinsky@gmail.com>
//
// 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();
    }
}

注意:

我在每个实现的正上方直接添加了版权声明和许可证前言,以便为代码的原作者归功并促进公平使用。

IANAL:

唯一正确包含版权的是gcc,msvc版本缺少年份,clang(llvm)应该由贡献者手动分配版权。然而,我不是专家,可能错过或误解了分享代码的方式。如果有这样的情况,请告诉我,我会立即进行更改。

此外,据我所知,Apache-2.0仅与GPLv3在单向上兼容。你可以在GPLv3下转授权我的整个代码,但不能在Apache-2.0下转授权。

工作证明:

我用来展示我的版本工作的示例是从learncpp中取得的:

  1. Arduino 1.8.9
  2. x86 msvc v19.28
  3. x86-64 clang (trunk)
  4. x86-64 gcc (trunk)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接