提问者:小点点

使用std::vector进行std::数组初始化


假设我有一个在编译时已知大小的 std::vector,我想把它变成一个 std::array。我该怎么做?是否有标准功能可以执行此操作?

到目前为止,我最好的解决方案是:

template<class T, std::size_t N, class Indexable, std::size_t... Indices>
std::array<T, N> to_array_1(const Indexable& indexable, std::index_sequence<Indices...>) {
  return {{ indexable[Indices]... }};
}

template<class T, std::size_t N, class Indexable>
std::array<T, N> to_array(const Indexable& indexable) {
  return to_array_1<T, N>(indexable, std::make_index_sequence<N>());
}

std::array<Foo, 123> initFoos {
  std::vector<Foo> lst;
  for (unsigned i = 0; i < 123; ++i)
    lst.push_back(getNextFoo(lst));
  return to_array<Foo, 123>(lst); // passing lst.begin() works, too
}

这个应用程序类似于用非默认构造的类型填充std::array(没有变量模板):我也有一个非默认构造的类型,所以我需要在数组初始化时计算实际值。然而,与这个问题相反,对我来说,这些值不仅仅是指数的函数,也是前面的值的函数。与一系列函数调用相比,使用循环可以更容易地构建我的值。所以我在一个循环中构造元素,并把它们放在一个向量中,然后我想用这个向量的最终状态来初始化数组。

以上似乎编译和工作正常,但也许有办法改进它。

  1. 也许我可以巧妙地使用一些我不知道的标准库功能。
  2. 也许我可以以某种方式避免帮助程序函数。
  3. 也许我可以以某种方式表述它,使其与元素的移动语义一起使用,而不是上面使用的复制语义。
  4. 也许我可以避免使用 operator[] 进行随机访问,而是仅使用前向迭代器语义,以便它也适用于 std::set 或 std:forward_list 作为输入。
  5. 也许我应该停止使用 std::
  6. vector,而是使用 std:array 来表达我的目标

相关问题:

  • 将std::vector复制到std::array中,该数组不采用没有默认构造函数的类型,因此在默认初始化后复制是可行的,但对我来说不可行。
  • 使用非默认的可构造类型(无可变模板)填充std::array,该类型根据索引独立计算每个元素,因此它试图避免使用中间容器。答案使用多种模板,即使标题要求避免使用这些模板

共1个答案

匿名用户

我提议:

template<typename T, typename Iter, std::size_t... Is>
constexpr auto to_array(Iter& iter, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
    return {{ ((void)Is, *iter++)... }};
}

template<std::size_t N, typename Iter,
         typename T = typename std::iterator_traits<Iter>::value_type>
constexpr auto to_array(Iter iter)
-> std::array<T, N> {
    return to_array<T>(iter, std::make_index_sequence<N>{});
}

这将从迭代器中导出元素类型,并将复制与移动语义留给调用者——如果调用者想要移动,他们可以通过std::move_iterator或类似方式选择:

auto initFoos() {
    constexpr unsigned n{123};

    std::vector<Foo> lst;
    for (unsigned i{}; i != n; ++i) {
        lst.push_back(getNextFoo(lst));
    }

    // copy-init array elements
    return to_array<n>(lst.cbegin());

    // move-init array elements
    return to_array<n>(std::make_move_iterator(lst.begin()));
}

在线演示

编辑:如果有人想要覆盖推导出的元素类型,如评论中所示,那么我建议:

template<typename T, typename Iter, std::size_t... Is>
constexpr auto to_array(Iter& iter, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
    return {{ ((void)Is, T(*iter++))... }};
}

template<std::size_t N, typename U = void, typename Iter,
         typename V = typename std::iterator_traits<Iter>::value_type,
         typename T = std::conditional_t<std::is_same<U, void>{}, V, U>>
constexpr auto to_array(Iter iter)
-> std::array<T, N> {
    return to_array<T>(iter, std::make_index_sequence<N>{});
}

这使得元素类型是可选的,但使其成为第二个参数而不是第一个参数,因此用法类似于to_array