std::initializer_list 的实现

Posted

技术标签:

【中文标题】std::initializer_list 的实现【英文标题】:Implementation of std::initializer_list 【发布时间】:2013-08-12 10:16:48 【问题描述】:

我一直在研究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

看来这不是真的language feature as it depends on the standard library?

我的问题的重点是为什么名称如此重要以及它试图传递给构造函数的那 3 个参数是什么?

【问题讨论】:

仅仅因为标准库中有某些东西并不意味着它没有内置到整个语言中。 C++ 完全包含语言核心标准库。 new 运算符是语言功能吗?这取决于std::bad_allocdynamic_cast 呢,它依赖于std::bad_casttypeid 运算符产生std::type_info 类型的对象?标准中有一个完整的部分称为“语言支持库”。 将缺少的模板参数添加到ArrayPrinter&lt;int&gt; ap 1,2,3 ; 后,它按预期工作:live example @DrYap No std::initializer_list 在我的示例中创建。出现在那里的1,2,3(是一个braced-init-list并且)用于通过调用模板ctor来构造一个MyNamespace::InitializerList&lt;int&gt;,方法与ArrayPrinter&lt;int&gt; ap( MyNamespace::InitializerList&lt;int&gt;(1,2,3) );相同 这篇博文是我实现 std::initializer_list 的经验。 fauxfoefriend.blogspot.com/2013/05/uniform-initialization.html 【参考方案1】:

名称很重要,因为标准说它很重要。标准需要某种方式让您能够说,“这个构造函数可以传递一个包含 T 类型值序列的花括号初始化列表”。这种方式被命名为“std::initializer_list”。

您不能创建具有initializer_list 的所有语言 属性的类。您可以制作一个满足标准 [initializer.list.syn] 部分指定的类型的条件。但是你会注意到唯一指定的构造函数是 default 构造函数。使用实际元素创建initializer_list 的唯一方法依赖于编译器,而不是用户级代码。

因此,您无法复制有关 initializer_list 的所有内容。就像您无法复制 std::type_info 一样。 C++ 标准库不是可选的

【讨论】:

@kuroineko Arduino 缺少标准要求的兼容独立实施的什么? @underscore_d:嗯,&lt;initializer_list&gt; 在独立实现所需的标头列表中。我不使用 Arduino,但如果他们不提供,那么他们甚至没有提供独立的 C++11 实现。 快速搜索表明他们(即avr-gcc)确实没有。在原始评论中说出来会更清楚,也不会占用更多空间!无论如何,不​​幸的是学习。 抱歉我的厚颜无耻的评论,我只是偶然发现了这个问题,并没有详细说明。【参考方案2】:

这个答案并不完全准确。 可以创建一个功能齐全的std::initializer_list - 它只需要满足目标编译器的特定要求。

对于 GCC 和 clang,该要求是私有 ctor。这是 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();

【讨论】:

【参考方案3】:

当我自己实现 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:

    Arduino 1.8.9 x86 msvc v19.28 x86-64 clang (trunk) x86-64 gcc (trunk)

【讨论】:

以上是关于std::initializer_list 的实现的主要内容,如果未能解决你的问题,请参考以下文章

std::initializer_list 返回值的生命周期

为啥 `std::initializer_list` 不提供下标运算符?

为啥 std::initializer_list 不是内置语言?

为啥 std::min(std::initializer_list<T>) 按值接受参数?

std::initializer_list 作为函数参数

如何将 C 数组转换为 std::initializer_list?