【问题标题】:How to fill a C++ container using a (lambda) function?如何使用(lambda)函数填充 C++ 容器?
【发布时间】:2018-12-20 18:36:05
【问题描述】:

我想用指向对象的指针初始化一个容器。我目前有一个这样的循环:

for(int i=0;i < n;i++) {
    container.push_back(new Object());
}

哪个 C++ 操作(即类似于std::transform)适合替换此循环并使用n 新创建的对象初始化容器?

【问题讨论】:

  • 你要std::generate_n(std::back_inserter(container), n, /*my lambda*/)吗?
  • 最好使用智能指针而不是原始的拥有指针。
  • @Jarod42 公平地说,我们不确定container 不包含智能指针。
  • @FrançoisAndrieux 带有隐式构造函数的智能指针?把它们都烧掉!
  • @FrançoisAndrieux:一个接受非显式转换的智能指针...哎呀(emplace_back 可能有所不同,但是如果容器“调整大小”发生异常,它会泄漏)。

标签: c++ stl containers


【解决方案1】:

使用std::generate:

constexpr int n = 10;
std::vector<Object*> v1(n);
std::generate(v1.begin(), v1.end(), [](){ return new Object(); });

std::generate_n:

std::vector<Object*> v2;
v2.reserve(n); // pre-allocate sufficient memory to prevent re-allocations
               // (you should have done in original loop approach as well)
std::generate_n(std::back_inserter(v2), n, [] { return new Object(); });

【讨论】:

  • 我认为一个简短的例子会大大改善这个答案。
  • 注意,为了“推回”元素,你需要std::back_inserter
  • 我想我会使用std::generate,因为在使用之前以合适的容量初始化容器很便宜,然后在填充时不需要调整大小。
  • @Ronv(n, new T()) 会插入指向所有向量元素的相同指针,这已经取消了 std::fill(_n) 的资格...
  • @Ron 我不会... OP 不想选择 generate_n 因为他担心效率损失(请参阅上面的 cmets),但是加上储备,这种方法实际上甚至更好...
【解决方案2】:

您可以将 std::generate_nstd::back_inserter 与 lambda 一起使用。

std::generate_n(std::back_inserter(container), n, [] { return new Object(); });

【讨论】:

    【解决方案3】:

    目标是这样的语法:

    std::vector<Object*> v1 = generate([](auto&&){ return new Object; }, 10).make_container();
    

    我们说我们要生成 10 个具有特定 lambda 的元素,然后我们创建一个请求类型的容器。

    它需要一些样板文件。首先是一个输入迭代器,对一个函数进行计数和调用:

    template<class F>
    struct generator_iterator {
      F f;
      std::size_t i = 0;
    
      using self=generator_iterator;
      friend bool operator==(self const& lhs, self const& rhs){ return lhs.i==rhs.i; }
      friend bool operator!=(self const& lhs, self const& rhs){ return lhs.i!=rhs.i; }
      using reference=std::result_of_t<F const&(std::size_t const&)>;
      using value_type=std::decay_t<reference>;
      using difference_type=std::ptrdiff_t;
      using pointer=value_type*;
      using iterator_category=std::input_iterator_tag;
    
      self& operator++(){++i; return *this;}
      self operator++(int){auto tmp=*this; ++*this; return tmp;}
      reference operator*()const{ return f(i); }
      pointer operator->()const { return std::addressof(f(i)); }
    
      friend difference_type operator-( self const& lhs, self const& rhs ) { return lhs.i-rhs.i; }
      self& operator-=( difference_type rhs )& {
        i-=rhs;
        return *this;
      }
      self& operator+=( difference_type rhs )& {
        i+=rhs;
        return *this;
      }
      friend difference_type operator+( self lhs, difference_type rhs ) {
        lhs += rhs;
        return lhs;
      }
      friend difference_type operator-( self lhs, difference_type rhs ) {
        lhs -= rhs;
        return lhs;
      }
    
    };
    

    接下来,一个范围原语,带有一个.make_container() 方法,可让您通过显式或隐式传递类型将范围转换为容器:

    template<class It>
    struct range_t {
      It b, e;
      It begin() const { return b; }
      It end() const { return e; }
    
    private:
      struct container_maker {
        range_t const* self;
        template<class C>
        operator C()&& {
          return {self->begin(), self->end()};
        }
      };
    public:
      container_maker make_container()const{
        return {this};
      }
      // C is optional
      template<class C>
      C make_container()const{
        return make_container();
      }
    };
    template<class It>
    range_t<It> range( It s, It f ) {
      return {std::move(s), std::move(f)};
    }
    

    然后我们将它们粘合在一起:

    template<class F>
    auto generate( F&& f, std::size_t count ) {
      generator_iterator<std::decay_t<F>> e{f, count};
      generator_iterator<std::decay_t<F>> b{std::forward<F>(f)};
      return range( std::move(b), std::move(e) );
    }
    

    然后编译:

    std::vector<Object*> v1 = generate([](auto&&){ return new Object; }, 10).make_container();
    

    Live example.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-15
      相关资源
      最近更新 更多