【问题标题】:create container from another container, applying each element some function in c++从另一个容器创建容器,在 C++ 中对每个元素应用一些函数
【发布时间】:2014-02-21 19:22:22
【问题描述】:

我的问题很简单,看例子:

std::array<int,6> a = {{0,1,2,3,4,5}}; // -- given container.
auto F = []( int i ) { return  i*i; }; // -- given function.

std::vector<int> v;  // need create

// my solution:
v.reserve( a.size () );
for( std::size_t i = 0; i < a.size(); ++i )
    v.push_back( F(a[i]) ); 



// but I need something like
   std::vector<int>v( a.begin(), a.end(), <|applying each element to F|> );

我可以创建类似上面的容器,而不是显式调用 Reserve 和任何重新分配吗?

编辑

  1. 我想避免保留;因为在其他情况下我的第一个解决方案很好 对我来说:)
  2. 我想避免任何调整大小;因为它每个都被初始化 元素默认为ctor。
  3. 我将在实际项目中使用它,其中可能包含许多 3 方库(boost、Soft-STL、...)。

【问题讨论】:

  • std::transform 不会这样做吗?
  • transform iterators 可能在这里有用。
  • 我也想避免使用 v.reserve(a.size())。

标签: c++ c++11 iterator containers stl-algorithm


【解决方案1】:

标准算法std::transform正是这样做的

std::vector<int> v(a.size());
std::transform(
   std::begin(a), std::end(a),
   std::begin(v),
   F
);

如果您愿意,可以从一个空向量开始并使用std::back_inserter

std::vector<int> v;
std::transform(
   std::begin(a), std::end(a),
   std::back_inserter(v),
   F
);

但是如果你这样做,你就会让自己遭受不必要的重新分配(除非你首先reserve,就像你最初的尝试一样)。你可以自己决定你的优先级是什么。

【讨论】:

  • 我不能使用 v(a.size() ),因为我真正的容器元素不是 int,而是一些大类。它需要我两行。
  • @Khurshid:当然,这取决于你。您可以在这里选择所有选项。只需要两行是什么意思?为什么?源代码中的空间非常便宜! :)
  • @Khurshid 不过,调用reserve + bqck_inserter 示例似乎是一个不错的选择。
  • back_inserter 调用 push_back 吗?如果是,则它不是有效的内存 usafe。
  • 我想要的是,可读性更高的解决方案,而且没有任何性能成本。显式写入保留 - 不可读,仅用于性能。
【解决方案2】:

使用std::transform:

#include <algorithm> // std::transform
#include <iterator>  // std::back_inserter

....

transform(a.begin(), a.end(), back_inserter(v), F);

您可能需要先致电v.reserve(asize()) 以避免重新分配。

【讨论】:

  • 暗中使用 ADL。
  • @KerrekSB 我感觉特别懒惰。周五是新的周日。
  • @KerrekSB 什么是 ADL? :P
  • @herohuyongtao 这就是不合格的电话back_inserter(v)工作的原因。
  • @Snps: 和transform
【解决方案3】:

另一种解决方案是使用boost::transform_iterator。好处是您可以将迭代器传递给容器构造函数。与使用std::back_inserter 或必须在目标上调用reserveresize 相比,这避免了内存重新分配。一言以蔽之:

std::vector<int> result(
      boost::make_transform_iterator(std::begin(a), F)
    , boost::make_transform_iterator(std::end(a), F)
    );

您可以实现更简洁的语法,如下所示:

std::vector<int> result(transform_range(a, F));

transform_range 实现:

template<class Iterator>
struct AutoSequence
{
    Iterator const beg_, end_;

    template<class T>
    operator std::vector<T>() const {
        return {beg_, end_};
    }
};

template<class Function, class InSeq>
auto transform_range(InSeq const& in) -> AutoSequence<decltype(boost::make_transform_iterator<Function>(in.begin()))> {
    return {
          boost::make_transform_iterator<Function>(std::begin(in))
        , boost::make_transform_iterator<Function>(std::end(in))
        };
}

template<class Function, class InSeq>
auto transform_range(InSeq const& in, Function&& f) -> AutoSequence<decltype(boost::make_transform_iterator(in.begin(), f))> {
    return {
          boost::make_transform_iterator(std::begin(in), f)
        , boost::make_transform_iterator(std::end(in), f)
        };
}

【讨论】:

  • 完全没有必要使用第三方库。这给了你什么?此处保存的一行代码,在标题中添加了一千行。太棒了。
  • @LightnessRacesinOrbit 为我哭泣。
  • 那么,您是否衡量了该性能优势?您还测量了编译时间的影响吗?
  • @MaximYegorushkin:啊哈!一个道理!这也是一个不错的选择,尽管您仍然必须非常努力地说服我这个微小的微优化值得引入第三方库来完成这项工作。不幸的是,您的回答并没有让 OP 意识到这一点。
  • #define BOOST_RESULT_OF_USE_DECLTYPE 需要 lambda )
猜你喜欢
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-20
相关资源
最近更新 更多