27

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

5 回答 5

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

活生生的例子。

可悲的是,由于function_input_iterator不使用 Boost.ResultOf,因此您需要一个函数指针或具有嵌套result_type的函数对象类型。Lambdas,无论出于何种原因,都没有。您可以将 lambda 传递给一个std::function(或boost::function)对象,该对象定义了它。这是一个例子std::function。只能希望 Boost.Iterator 有朝一日会使用 Boost.ResultOf,decltype如果BOOST_RESULT_OF_USE_DECLTYPE已定义,它将使用。

于 2012-09-20T11:53:00.247 回答
6

世界太大了,C++ 无法为所有事情提供解决方案。然而,C++ 并不想成为一个巨大的超市,里面装满了满足各种口味的即食食品。相反,它是一个设备齐全的小型厨房,C++ Master Chef 可以在其中烹制任何想要的解决方案。

这是序列生成器的一个愚蠢且非常粗略的示例:

#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());

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

于 2012-09-20T11:52:10.497 回答
2

如果您使用的编译器支持您在问题中使用的 lambda,那么很有可能它还包括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);

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

于 2012-09-20T16:44:05.493 回答
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

于 2012-09-20T21:41:02.953 回答
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;
    }
}
于 2012-09-20T11:48:20.573 回答