将vector<T>转换为initializer_list <T>
每个人都从std::initializer_list
创buildstd::vector
,但是std::initializer_list
呢?
例如。 如果你使用std::initializer_list
作为参数:
void someThing(std::initializer_list<int> items) { ... }
有些时候,你有你的项目在一个vector<T>
而不是一个文字列表:
std::vector<int> v; // populate v with values someThing(v); // boom! No viable conversion etc.
更一般的问题是:如何从STL迭代中创build一个stl::initializer_list
,而不仅仅是std::vector
。
我发布了一种似乎可行的方式,但不幸的是,由于initializer_lists被视为对本地范围的值副本的引用,导致内存访问违规。
这是一个替代scheme。 为每个可能数量的项目生成一个单独的函数和一个独立的静态初始化器列表,这些项目用一个参数包计数。 这不是线程安全的,并且使用const_cast(这被认为是非常糟糕的)来写入静态的initializer_list内存。 不过,它在gcc和clang中都干净利落。
如果因为一些不明确的原因,你需要解决这个问题,没有其他的select,你可以尝试这个黑客。
#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; }
答案是否定的,你不能这样做。
std::initializer_list<T>
types的对象是一个轻量级代理对象,它提供对Ttypes对象数组的访问std::initializer_list
对象在下列情况下自动构造 :
- 在列表初始化中使用了一个braced-init-list,包括函数调用列表初始化和赋值expression式(不要和构造函数初始化列表混淆)
- 一个braced-init-list绑定到auto,包括一个ranged for循环
就库支持而言, std::initializer_list
只有一个构造一个空列表的默认构造函数, 其迭代器是常量 。 缺less一个push_back()
成员意味着你不能申请例如一个std::copy
与一个std::back_inserter
迭代器适配器来填充它,也不能通过这样的迭代器直接分配:
#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 }
现场示例
如果你看看标准容器,除了在构造函数/插入器中接受std::initializer_list
之外,它们都具有构造函数/插入器,并且可以将initializer_list
函数委托给相应的迭代器对函数。 例如,libc ++中的std::vector<T>::insert
函数就是这么简单的一行代码 :
iterator insert(const_iterator __position, initializer_list<value_type> __il) {return insert(__position, __il.begin(), __il.end());}
你应该沿着类似的路线修改你的代码:
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 }
当你有一个向量而不是文字列表中的项目的时候:
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
显然不,这是不可能的 。 没有这样的构造函数(我相信有很好的理由), std::initializer_list
是一个奇怪的生物。
你可以做的是改变一些someThing()
接受一对迭代器。 这样你就可以得到你想要的东西,只要你可以改变这个函数的签名(它不在第三方库中)。
是的,你可以做到这一点,但你不想这样做,因为你必须这样做是非常愚蠢的。
首先,确定列表的最大长度。 必须有一个最大长度,因为size_t
不是无限的。 理想情况下,find一个更好的(小),如10。
其次,编写采用运行时间整数的幻码开关代码,并将其映射到编译时间整数,然后用该编译时间整数调用模板类或函数。 这样的代码需要一个最大整数大小 – 使用上面的最大长度。
现在,魔法将vector的大小切换到编译时间长度。
创build一个从0
到length-1
的编译时序列。 在initializer_list
构造中解压该序列,每次在std::vector
上调用[]
。 用你所得到的initializer_list
调用你的函数。
以上是棘手和荒谬的,大多数编译器会炸毁它。 有一个步骤,我不确定的合法性 – 是一个initializer_list
的build设一个合法的地方做拆迁varardic论证?
下面是一个魔术开关的例子: 我可以分开编译时策略的创build和使用位置吗?
下面是一个索引或序列技巧的例子: 来自元组的构造函数参数
这篇文章应该只是理论上的兴趣,因为实际上这是解决这个问题的一个非常愚蠢的方法。
做一个随意的迭代更困难,没有做n ^ 2工作。 但是,因为上面已经足够荒谬了,任意可迭代的版本会更可笑…(也许有一堆lambda – 得到它,所以参数按顺序评估可能会很棘手。是否有序列点之间初始化列表的各种参数的评估?)
如果你不介意副本,那么我认为这样的事情会起作用:
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); }
对于一个vector,你可以使用指针算术。
Foo( std::initializer_list< _Type >( aVector.data(), aVector.data() + myVector.size() ) ) ;
你可以把它包装成一个通用的函数。
template< typename _Type > auto InitList( const _Type * begin, size_t size ) { return std::initializer_list< _Type >( begin, begin + size ) ; }
调用这样的function。
Foo( InitList( aVector.data(), aVector.size() ) ) ;