代码之家  ›  专栏  ›  技术社区  ›  Richard Hodges

构造动态大小的std::initializer\u列表,第二部分

  •  3
  • Richard Hodges  · 技术社区  · 7 年前

    this question 我开始想是否有办法创造一个 std::initializer_list std::vector .

    下面是代码的第一次尝试:

    #include <initializer_list>
    #include <vector>
    #include <iostream>
    #include <array>
    #include <stdexcept>
    
    namespace impl
    {
        template<class T, std::size_t...Is>
        auto as_init_list(std::vector<T> const& v, std::index_sequence<Is...>)
        {
            auto ret = std::initializer_list<T>
            {
                v[Is]...
            };
            return ret;
        }
    
        template<class T, std::size_t N>
        auto as_init_list(std::vector<T> const& v)
        {
            auto ret = as_init_list(v, std::make_index_sequence<N>());
            return ret;
        }
    
        template<class T, std::size_t...Is>
        constexpr auto as_init_list_vtable(std::index_sequence<Is...>)
        {
            using ftype = std::initializer_list<T>(*)(std::vector<T> const&);
            auto ret = std::array<ftype, sizeof...(Is)>
            {{
                &as_init_list<T, Is>...
            }};
            return ret;
        }
    
        template<class T, std::size_t N>
        constexpr auto as_init_list_vtable()
        {
            auto ret = as_init_list_vtable<T>(std::make_index_sequence<N>());
            return ret;
        }
    }
    
    template<class T, std::size_t Limit = 100>
    auto as_init_list(std::vector<T> const& vec)
    -> std::initializer_list<T>
    {
        if (vec.size() >= Limit)
            throw std::invalid_argument("too long");
        static const auto table = impl::as_init_list_vtable<T, Limit>();
        auto ret = table[vec.size()](vec);
        return ret;
    }
    
    int main()
    {
        std::vector<int> v = { 1, 2, 3, 4 };
        auto i = as_init_list(v);
        for (auto&& x : i)
        {
            std::cout << x << '\n';
        }
    }
    

    当然,正如一半人所料,结果似乎是:

    4200240
    32765
    0
    0
    

    http://coliru.stacked-crooked.com/a/1bf92111619317dd

    在这种情况下(承认是不寻常和反常的情况),我似乎违反了一些关于初始值设定项列表中项的元素的生存期的规则,但乍一看,代码应该是有效的(因为保证了RVO)。

    2 回复  |  直到 7 年前
        1
  •  5
  •   NathanOliver    7 年前

    auto as_init_list(std::vector<T> const& v, std::index_sequence<Is...>)
    {
        auto ret = std::initializer_list<T>
        {
            v[Is]...
        };
        return ret;
    }
    

    { v[Is]... } ret . 一次 超出范围阵列被摧毁,剩下一个悬空 std::initializer_list . 这包括在 [dcl.init.list]/6

    数组与任何其他临时对象具有相同的生存期([类别:临时]),除了初始化 initializer_­list 对象扩展数组的生存期,就像将引用绑定到临时对象一样。[示例:

    typedef std::complex<double> cmplx;
    std::vector<cmplx> v1 = { 1, 2, 3 };
    
    void f() {
      std::vector<cmplx> v2{ 1, 2, 3 };
      std::initializer_list<int> i3 = { 1, 2, 3 };
    }
    
    struct A {
      std::initializer_list<int> i4;
      A() : i4{ 1, 2, 3 } {}            // ill-formed, would create a dangling reference
    };
    

    为了 v1 v2 ,初始化器\u list对象是函数调用中的参数,因此为 { 1, 2, 3 } 具有完整的表达式生命周期。 i3 ,的 初始值设定项列表 对象是一个变量,因此数组在变量的生存期内保持不变 . 为了 i4 ,的 初始值设定项列表 对象在构造函数的 就像通过将临时数组绑定到引用成员,所以程序的格式是错误的([class.base.init类]). 结束示例][注意:如果可以这样分配具有相同初始值设定项的显式数组,则实现可以在只读内存中自由分配数组。[尾注]

    重点矿山

    你必须延长 雷特 的生存期,直到您使用它以避免未定义的行为。

        2
  •  6
  •   Vittorio Romeo    7 年前

    gcc(主干)

    auto foo()
    {
        return std::initializer_list<int>{0, 1, 2};
    }
    
    warning: returning temporary initializer_list does not extend the lifetime of the
             underlying array [-Winit-list-lifetime]
    
         return std::initializer_list<int>{0, 1, 2};
                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    

    live example on wandbox.org


    这是因为 std::initializer_list std::初始值设定项列表 实例。

    as_init_list ret 作为 static 围绕这个问题: http://coliru.stacked-crooked.com/a/e0ef17af8b398fb7

    推荐文章