【问题标题】:Is it not possible to convert a std::vector<T> to an std::initializer_list<T>?是否不可能将 std::vector<T> 转换为 std::initializer_list<T>?
【发布时间】:2015-01-22 20:25:00
【问题描述】:

我会认为有人可以做到这一点?然而,情况似乎并非如此。为什么?还是我做错了什么?

#include <vector>
#include <initializer_list>
#include <iostream>

using namespace std;

void fn(std::initializer_list<int> i)
{
  for(int ivalue:i)
  {
    cout << ivalue << endl;
  }
}

int main()
{
  fn({1,2,3});
  fn(vector<int>{4,5,6})
  return 0;
}

demo

我问这个的原因是因为我有一个接受初始化列表的类,并且我从它派生,但需要在将初始化列表传递给基之前对其进行处理。我怎样才能做到这一点?

【问题讨论】:

  • 向量不是初始化列表。虽然两者都是范围,但您可以在两者上使用 begin/end 或 for 循环并以这种方式共享代码。
  • 是的,我知道向量不是初始化列表,但它是可转换的?
  • 不,它不是(可转换的)。它不应该是。 initializer_list 的创建是为了从它们创建像 vector 这样的容器,而不是相反。
  • @bolov,那么我该如何修改对基本构造函数的调用呢?除非有其他方法可以使用向量,否则这是一个过于受限的习语。
  • @Adrian initializer_list 专门用于传递用 { 和 } 拼写的列表。统一两者的抽象概念是范围。有些人正在研究一个类(与view 相同),该类可以从任何范围构造并且可以扮演您想要的角色。这根本不是 initializer_list 应该的样子。

标签: c++ c++11 vector stdvector initializer-list


【解决方案1】:

有时旧方法是最好的方法:只需在范围内传递:

void fn(std::initializer_list<int> i) {
    fn(i.begin(), i.end());
}

template <typename It>
void fn(It it, It end) {
    for (; it != end; ++it) {
        std::cout << *it << std::endl;
    }
}

fn({1, 2, 3});

std::vector<int> v{1, 2, 3};
fn(std::begin(v), std::end(v));

对于您的具体问题......无论如何,您的构造器都必须迭代它,只需将该操作委托给迭代器对构造器:

Foo(std::initializer_list<T> init)
: Foo(init.begin(), init.end())
{ }

template <typename It>
Foo(It it, It end)
{
    // here the magic happens
}

【讨论】:

  • 您也可以传递一个“新式”范围,无需拆分成一对迭代器。这样,fn 的主体可以像在 OP 中一样保留。
  • @MarcGlisse 你说的这个新风格系列是什么?
  • template&lt;class R&gt;void fn(R const&amp;r){for(auto value:r)cout&lt;&lt;value&lt;&lt;endl;}
  • @MarcGlisse,哦,那个。我以为你指的是某种类型的课程。
  • @Marc 在 C++1z 中变成for (value: r),其中valueauto&amp;&amp;
【解决方案2】:

C++ 缺少一个简单的“查看连续的内存块”类。这是一个:

template<class T>
struct ro_array_view {
  T const* b_ = nullptr;
  T const* e_ = nullptr;
  size_t size() const { return end()-begin(); }
  T const* begin() const { return b_; }
  T const* end() const { return e_; }
  T const& operator[](size_t i)const{ return begin()[i]; }
  bool empty()const{return begin()==end();}
  T const& front() const { return *begin(); }
  T const& back() const { return *std::prev(end()); }

  // annoying numbers of constructors:
  struct from_container_tag {};
  template<class O>
  ro_array_view( from_container_tag, O&& o ):
    ro_array_view(o.data(), o.size()) {}

  template<class...A>
  ro_array_view( std::vector<T,A...> const& o )
    :ro_array_view(from_container_tag{},o) {}
  ro_array_view( std::initializer_list<T> const& il )
    :ro_array_view(il.begin(), il.size()) {}
  template<size_t N>
  ro_array_view( std::array<T, N> const& o )
    :ro_array_view(from_container_tag{},o) {}
  template<size_t N>
  ro_array_view( std::array<T const, N> const& o )
    :ro_array_view(from_container_tag{},o) {}
  template<size_t N>
  ro_array_view( T const(&arr)[N] )
    :array_view( arr, N ) {}

  // penultimate constructor of most paths:
  ro_array_view( T const* arr, size_t N )
    :ro_array_view(arr, arr+N) {}
  // terminal constructor:
  ro_array_view( T const* b, T const* e )
    :b_(b),e_(e) {}
};

现在只需使用ro_array_view&lt;int&gt;,它就会将传入的参数转换为一对指针,并向其公开一个只读的类似容器的接口。

乍一看这似乎有点过头了,但你会发现它是一个非常常用的函数。大量采用 std::vector&lt;T&gt; const&amp; 的函数应该按值采用 ro_array_view&lt;T&gt;

ro_array_view 有点不同(它的方法大多仍然是const,但它存储T* 而不是T const*。我称之为rw_array_view,然后将array_view a using 有条件地使用 ro_array_viewrw_array_view 的别名。添加从 rw_array_viewro_array_view 的转换 ctor 以完成项目。

我有两种不同的类型,而不是一种,因为rw_array_viewro_array_view 具有来自某些容器的不同构造函数。 rw_array_view&lt;T&gt; 可以从 vector&lt;T,A...&gt;&amp; 构造,而 ro_array_view&lt;T&gt; 需要 vector&lt;T, A...&gt;const&amp;std::array 有点糟糕,因为const Tstd::array 中的有效类型。 ro_array_view 用于大多数用途,rw_array_view 适用于我们想要修改内容而不修改容器的情况。

【讨论】:

    猜你喜欢
    • 2019-10-20
    • 2016-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-16
    • 2017-09-01
    • 2018-02-02
    • 1970-01-01
    相关资源
    最近更新 更多