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

时间:2012-09-20 11:30:53

标签: c++ c++11 iterator initialization stdvector

在许多语言中,有一些生成器可以帮助初始化集合。在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);

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

4 个答案:

答案 0 :(得分:15)

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

对于您的具体示例,您可以像这样使用Boost.Iterator的counting_iterator

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

或者甚至使用像这样的Boost.Range:

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 << " ";
}

Live example.

遗憾的是,由于function_input_iterator不使用Boost.ResultOf,因此需要一个函数指针或一个具有嵌套result_type 的函数对象类型。无论出于何种原因,兰巴达都没有。您可以将lambda传递给std::function(或boost::function)对象,该对象定义了该对象。 Here's an example with std::function。人们只能希望Boost.Iterator有一天会使用Boost.ResultOf,如果定义了decltype,它将使用BOOST_RESULT_OF_USE_DECLTYPE

答案 1 :(得分:2)

如果您在问题中使用支持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);

至于为什么这不包含在标准库中,我甚至无法猜测。我认为这可以看作支持范围的论据,因此算法将采用一个范围,我们可以通过一种简单的方法从开始/结束对或开始/计数对创建范围。我不确定我是否完全赞同这一点 - 范围似乎在某些情况下效果很好,但在其他情况下它们几乎没有任何意义。我不确定没有更多的工作,我们的答案确实比一对迭代器好很多。

答案 2 :(得分:2)

没有人提到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

答案 3 :(得分:0)

您可以使用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;
    }
}