C++ 将 vector<T> 转换为 initializer_list<T>

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18895583/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:19:55  来源:igfitidea点击:

Convert a vector<T> to initializer_list<T>

c++c++11stlinitializer-list

提问by Fil

Everyone creates std::vectorfrom std::initializer_list, but what about the other way around?

每个人都std::vector从创造std::initializer_list,但反过来呢?

eg. if you use a std::initializer_listas a parameter:

例如。如果您使用 astd::initializer_list作为参数:

void someThing(std::initializer_list<int> items)
{
...
}

There are times when you have your items in a vector<T>instead of a literal list:

有时您将项目放在一个vector<T>而不是文字列表中:

std::vector<int> v;
// populate v with values
someThing(v); // boom! No viable conversion etc.

The more general question is: how to create an stl::initializer_listfrom a STL iterable, not just std::vector.

更普遍的问题是:如何stl::initializer_list从 STL 可迭代对象创建一个,而不仅仅是std::vector.

采纳答案by fuzzyTew

I posted a way that seemed to work but unfortunately caused memory access violations because of how initializer_lists are treated as references to locally-scoped copies of values.

我发布了一种似乎有效的方法,但不幸的是,由于如何将 initializer_lists 视为对本地范围的值副本的引用,因此导致内存访问冲突。

Here's an alternative. A separate function and a separate static initializer list is generated for each possible number of items, which are counted with a parameter pack. This is not thread safe and uses a const_cast (which is considered very bad) to write into the static initializer_list memory. However, it works cleanly in both gcc and clang.

这是一个替代方案。为每个可能的项目数量生成一个单独的函数和一个单独的静态初始化列表,这些项目用参数包进行计数。这不是线程安全的,并且使用 const_cast(这被认为是非常糟糕的)写入静态初始化列表内存。但是,它在 gcc 和 clang 中都能正常工作。

If for some obscure reason you need this problem solved and have no other options, you could try this hack.

如果出于某种晦涩的原因,您需要解决此问题并且没有其他选择,则可以尝试此 hack。

#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <type_traits>
#include <vector>

namespace __range_to_initializer_list {

    constexpr size_t DEFAULT_MAX_LENGTH = 128;

    template <typename V> struct backingValue { static V value; };
    template <typename V> V backingValue<V>::value;

    template <typename V, typename... Vcount> struct backingList { static std::initializer_list<V> list; };
    template <typename V, typename... Vcount>
    std::initializer_list<V> backingList<V, Vcount...>::list = {(Vcount)backingValue<V>::value...};

    template <size_t maxLength, typename It, typename V = typename It::value_type, typename... Vcount>
    static typename std::enable_if< sizeof...(Vcount) >= maxLength,
    std::initializer_list<V> >::type generate_n(It begin, It end, It current)
    {
        throw std::length_error("More than maxLength elements in range.");
    }

    template <size_t maxLength = DEFAULT_MAX_LENGTH, typename It, typename V = typename It::value_type, typename... Vcount>
    static typename std::enable_if< sizeof...(Vcount) < maxLength,
    std::initializer_list<V> >::type generate_n(It begin, It end, It current)
    {
        if (current != end)
            return generate_n<maxLength, It, V, V, Vcount...>(begin, end, ++current);

        current = begin;
        for (auto it = backingList<V,Vcount...>::list.begin();
             it != backingList<V,Vcount...>::list.end();
             ++current, ++it)
            *const_cast<V*>(&*it) = *current;

        return backingList<V,Vcount...>::list;
    }

}

template <typename It>
std::initializer_list<typename It::value_type> range_to_initializer_list(It begin, It end)
{
    return __range_to_initializer_list::generate_n(begin, end, begin);
}

int main()
{
    std::vector<int> vec = {1,2,3,4,5,6,7,8,9,10};
    std::initializer_list<int> list = range_to_initializer_list(vec.begin(), vec.end());
    for (int i : list)
        std::cout << i << std::endl;
    return 0;
}

回答by TemplateRex

The answer is NO, you cannot do that.

答案是否定的,你不能那样做。

An object of type std::initializer_list<T>is a lightweight proxy object that provides access to an array of objects of type T. A std::initializer_listobject is automatically constructedwhen:

类型对象std::initializer_list<T>是轻量级代理对象,它提供对 T 类型对象数组的访问。在以下情况下std::initializer_list自动构造对象:

  • a braced-init-list is used in list-initialization, including function-call list initialization and assignment expressions (not to be confused with constructor initializer lists)
  • a braced-init-list is bound to auto, including in a ranged for loop
  • 花括号初始化列表用于列表初始化,包括函数调用列表初始化和赋值表达式(不要与构造函数初始化列表混淆)
  • 花括号初始化列表绑定到自动,包括在范围 for 循环中

As far as library support goes, std::initializer_listonly has a default constructor that constructs an empty list, and its iterators are constant. The lack of a push_back()member means you cannot apply e.g. a std::copywith a std::back_inserteriterator adaptor to fill it, and neither can you assign through such iterators directly:

就库支持而言,std::initializer_list只有一个构造空列表的默认构造函数,并且它的迭代器是 constant。缺少push_back()成员意味着您不能应用例如std::copy带有std::back_inserter迭代器适配器的 a 来填充它,也不能直接通过此类迭代器进行分配:

#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <vector>

int main() 
{
    auto v = std::vector<int> { 1, 2 };
    std::initializer_list<int> i;
    auto it = std::begin(i);
    *it = begin(v); // error: read-only variable is not assignable
}

Live Example

现场示例

If you look at the Standard Containers, in addition to accepting std::initializer_listin their constructors / inserters, they all have constructors / inserters taking an iterator pair, and the implementation is likely to delegate the initializer_listfunction to the corresponding iterator pair function. E.g. the std::vector<T>::insertfunction in libc++is this simple one-liner:

如果你看一下标准容器,除了std::initializer_list在它们的构造函数/插入器中接受之外,它们都有构造函数/插入器接受一个迭代器对,并且实现很可能将initializer_list函数委托给相应的迭代器对函数。例如std::vector<T>::insert,libc++ 中函数是这个简单的单行函数

 iterator insert(const_iterator __position, initializer_list<value_type> __il)
        {return insert(__position, __il.begin(), __il.end());}

You should modify your code along similar lines:

您应该按照类似的方式修改您的代码:

void someThing(std::initializer_list<int> items)
{
    someThing(items.begin(), items.end()); // delegate
}

template<class It>
void someThing(It first, It last)
{
    for (auto it = first, it != last; ++it) // do your thing
}

In times when you have your items in a vector instead of a literal list:

当您将项目放在向量中而不是文字列表中时:

std::vector<int> v = { 1, 2 };
auto i = { 1, 2 };
someThing(begin(v), end(v)); // OK
someThing(i); // also OK
someThing({1, 2}); // even better

回答by Ali

Apparently no, it is not possible. There is no such constructor (and I believe for good reasons), std::initializer_listis a weird creature.

显然不,这是不可能的。没有这样的构造函数(我相信有充分的理由),std::initializer_list是一种奇怪的生物。

What you could do instead is to change someThing()to accept a pair of iterators. In that way you get what you want, provided you can change the signature of that function (it isn't in a third party library, etc).

您可以做的是更改someThing()以接受一对迭代器。这样你就可以得到你想要的东西,只要你能改变那个函数的签名(它不在第三方库中,等等)。

回答by Yakk - Adam Nevraumont

Yes you can do this, but you don't want to do it, because how you have to do it is pretty silly.

是的,您可以这样做,但您不想这样做,因为您必须这样做非常愚蠢。

First, determine what the max length of your list is. There must be a max length, because size_tis not unbounded. Ideally find a better (smaller) one, like 10.

首先,确定列表的最大长度是多少。必须有一个最大长度,因为size_t不是无限的。理想情况下,找到一个更好(更小)的,例如 10。

Second, write magic switch code that takes a run-time integer, and maps it to a compile time integer, and then invokes a template class or function with that compile time integer. Such code needs a max integer size -- use the max length above.

其次,编写采用运行时整数的魔术开关代码,并将其映射到编译时整数,然后使用该编译时整数调用模板类或函数。这样的代码需要最大整数大小——使用上面的最大长度。

Now, magic switch the size of the vector into a compile time length.

现在,魔术般地将向量的大小转换为编译时间长度。

Create a compile time sequence of integers, from 0to length-1. Unpack that sequence in a initializer_listconstruction, each time invoking []on the std::vector. Call your function with that resulting initializer_list.

创建一个整数的编译时间序列,从0length-1。解压在该序列initializer_list建设,每次调用[]std::vector。使用结果调用您的函数initializer_list

The above is tricky and ridiculous and most compilers will blow up on it. There is one step I'm uncertain of the legality of -- is the construction of an initializer_lista legal spot to do varardic argument unpacking?

以上是棘手和荒谬的,大多数编译器都会炸毁它。有一个步骤我不确定其合法性 - 是否构建了initializer_list一个合法的点来进行可变参数解包?

Here is an example of a magic switch: Can I separate creation and usage locations of compile-time strategies?

下面是一个魔术开关的例子:我可以将编译时策略的创建和使用位置分开吗?

Here is an example of the indices, or sequence, trick: Constructor arguments from tuple

这是索引或序列技巧的示例:来自元组的构造函数参数

This post should only be of theoretical interest, because practically this is a really silly way to solve this problem.

这篇文章应该只是理论上的兴趣,因为实际上这是解决这个问题的一种非常愚蠢的方法。

Doing it with an arbitrary iterable is harder, without doing n^2 work. But as the above is already ridiculous enough, and the arbitrary iterable version would be more ridiculous... (Maybe with a pack of lambdas -- getting it so that the arguments are evaluated in order could be tricky. Is there a sequence point between the evaluation of the various arguments to an initializer list?)

在不做 n^2 工作的情况下,使用任意可迭代对象进行操作会更困难。但是由于上面已经足够荒谬了,而任意的可迭代版本会更荒谬......(也许有一组 lambdas - 获取它以便按顺序评估参数可能会很棘手。之间是否有序列点对初始化列表的各种参数的评估?)

回答by Paul Fultz II

If you don't mind copies, then I think something like this would work:

如果你不介意复制,那么我认为这样的事情会奏效:

template<class Iterator>
using iterator_init_list = std::initializer_list<typename std::iterator_traits<Iterator>::value_type>;

template<class Iterator, class... Ts>
iterator_init_list<Iterator> to_initializer_list(Iterator start, Iterator last, Ts... xs)
{
    if (start == last) return iterator_init_list<Iterator>{xs...};
    else return to_initializer_list(start+1, last, xs..., *start);
}