使用迭代函数调用初始化std::vector

Initializing std::vector with iterative function calls

本文关键字:std vector 初始化 函数调用 迭代      更新时间:2023-10-16

在许多语言中,都有生成器可以帮助初始化集合。在C++中,如果想统一初始化一个向量,可以写:

std::vector<int> vec(10, 42); // get 10 elements, each equals 42

如果一个人想在飞行中产生不同的价值呢?例如,用10个随机值或从0到9的连续数字初始化它?这种语法很方便,但它在C++11:中不起作用

int cnt = 0;
std::vector<int> vec(10, [&cnt]()->int { return cnt++;});

有没有一种通过迭代函数调用初始化集合的好方法?我目前使用的是这种难看的模式(不比循环可读/短多少):

std::vector<int> vec;
int cnt = 0;
std::generate_n(std::back_inserter(vec), 10, [&cnt]()->int { return cnt++;});

有一件事会有所帮助,它可以解释第一个构造函数的缺乏。我可以想象一个迭代器接受一个函数和多个调用,这样构造函数

vector ( InputIterator first, InputIterator last);

将适用。但我在标准库中没有找到这样的东西。我错过了吗?第一个构造函数没有达到标准还有其他原因吗?

遗憾的是,没有标准的设施可以做到这一点。

对于您的具体示例,您可以使用Boost。迭代程序的counting_iterator如下:

std::vector<int> v(boost::counting_iterator<int>(0),
boost::counting_iterator<int>(10));

甚至使用Boost。范围如下:

auto v(boost::copy_range<std::vector<int>>(boost::irange(0,10)));

(copy_range基本上只是return std::vector<int>(begin(range), end(range)),它是对只支持带两个迭代器的范围构造的现有容器采用全范围构造的好方法。)


现在,对于具有生成器函数(如std::rand)的通用情况,存在function_input_iterator。当递增时,它调用生成器并保存结果,然后在取消引用时返回。

#include <vector>
#include <iostream>
#include <cmath>
#include <boost/iterator/function_input_iterator.hpp>
int main(){
std::vector<int> v(boost::make_function_input_iterator(std::rand, 0),
boost::make_function_input_iterator(std::rand,10));
for(auto e : v)
std::cout << e << " ";
}

活生生的例子。

遗憾的是,由于function_input_iterator不使用Boost。ResultOf,您需要一个函数指针或具有嵌套result_type的函数对象类型。不管出于什么原因,兰达斯都没有。可以将lambda传递给定义它的std::function(或boost::function)对象。下面是一个使用std::function的示例。人们只能寄希望于Boost。迭代器将使用Boost。ResultOf有朝一日,如果定义了BOOST_RESULT_OF_USE_DECLTYPE,它将使用decltype

世界太大了,C++无法为所有东西提供解决方案。然而,C++不想成为一个巨大的超市,里面摆满了各种口味的即食食品。相反,这是一个设备齐全的小厨房,C++主厨可以在其中烹饪任何您想要的解决方案。

下面是一个愚蠢而粗糙的序列生成器示例:

#include <iterator>
struct sequence_iterator : std::iterator<std::input_iterator_tag, int>
{
sequence_iterator() : singular(true) { }
sequence_iterator(int a, int b) : singular(false) start(a), end(b) { }
bool singular;
int start;
int end;
int operator*() { return start; }
void operator++() { ++start; }
bool operator==(sequence_iterator const & rhs) const
{
return (start == end) == rhs.singular;
}
bool operator!=(sequence_iterator const & rhs) const
{
return !operator==(rhs);
}
};

现在你可以说:

std::vector<int> v(sequence_iterator(1,10), sequence_iterator());

同样,您可以编写一个更通用的小工具,"多次调用给定的函子"等(例如,通过模板复制获取函数对象,并将计数器用作重复计数器;取消引用调用函子)。

如果您在问题中使用的编译器支持lambdas,那么很可能它还包括std::iota,这至少会使计数情况更干净:

std::vector <int> vec(10);
std::iota(begin(vec), end(vec), 0);

对于这种情况(我认为还有很多其他情况),我们确实更喜欢iota_n

namespace stdx {
template <class FwdIt, class T>
void iota_n(FwdIt b, size_t count, T val = T()) {
for ( ; count; --count, ++b, ++val)
*b = val;
}
}

对于您的情况,您会使用以下内容:

std::vector<int> vec;
stdx::iota_n(std::back_inserter(vec), 10);

至于为什么它没有包含在标准库中,我真的无法猜测。我想这可以被视为支持范围的论点,所以算法会采用一个范围,我们可以用一种简单的方法从开始/结束对或开始/计数对创建一个范围。不过,我不确定我是否完全同意这一点——在某些情况下,范围似乎很有效,但在其他情况下,它们几乎没有意义。我不确定如果不做更多的工作,我们会得到一个比一对迭代器好得多的答案。

没有人提到boost::assign,所以我将在这里介绍它:

示例

#include <iostream>
#include <vector>
#include <boost/assign/std/vector.hpp> 
#include <cstdlib>
int main()
{
std::vector<int> v1;
std::vector<int> v2;
boost::assign::push_back(v1).repeat_fun(9, &rand);
int cnt = 0;
boost::assign::push_back(v2).repeat_fun(10, [&cnt]()->int { return cnt++;});
for (auto i : v1)
{
std::cout << i << ' ';
}
std::cout << std::endl;
for (auto i : v2)
{
std::cout << i << ' ';
}
}

输出

41 18467 6334 26500 19169 15724 11478 29358 26962
0 1 2 3 4 5 6 7 8 9

您可以使用SFINAE来形成一个表:

#include <iostream>
#include <vector>
template <int n> struct coeff    { static int const value = coeff<n-1>::value + 3; };
template <>      struct coeff<0> { static int const value = 0; };
template<int... values> struct c1 {static int const value[sizeof...(values)];};
template<int... values> int const c1<values...>::value[] = {values...};
template<int n, int... values> struct c2 : c2< n-1, coeff<n-1>::value, values...> {};
template<int... values> struct c2< 0, values... > : c1<values...> {};
template<int n> struct table : c2< n > {
static std::vector< unsigned int > FormTable()
{
return std::vector< unsigned int >( & c2< n >::value[0], & c2< n >::value[n] );
}
};
int main()
{
const auto myTable = table< 20 >::FormTable();
for ( const auto & it : myTable )
{
std::cout<< it << std::endl;
}
}