std::transform 到任意容器

Posted

技术标签:

【中文标题】std::transform 到任意容器【英文标题】:std::transform to arbitrary container 【发布时间】:2014-11-04 08:43:16 【问题描述】:

我想编写一个通用函数,它接收带有值[a1, .. , an]container1 并返回另一个带有值[convert(a1), .. , convert(an)]container2。 如果container2std::vector,那么问题很简单,std::transform 正是我想要的。以下函数可以处理任意container2container1

template<class ToType, class FromType>
ToType convert(const FromType& from)

    std::vector<typename ToType::value_type> tmp;
    std::transform(from.begin(), from.end(),
                   std::back_inserter(tmp),
                   [](const typename FromType::value_type& f) 
        return convert<typename ToType::value_type>(f);
    );
    return ToType(tmp.begin(), tmp.end());

但它确实添加了副本。有谁知道如何做得更好?

【问题讨论】:

为什么不直接使用ToType tmp; @Jarod42:并非所有容器都支持push_back 2Jarod42 我必须使用 std::back_inserter 是 std::transform 以防我想填充 std::vector。但是对于 std::back_inserter 我无法填写例如 std::set @MikeSeymour:所有容器都支持带有 2 个迭代器的构造函数吗? @Jarod42:除了array 之外的所有标准容器,只是因为这是一个聚合。 【参考方案1】:

查看this answer 至Is it possible to write a C++ template to check for a function's existence?。您可以使用 SFINAE 来检测目标容器的功能是否存在(例如 push_backinsert)或您的容器的插入器是否存在(例如 inserterback_inserter),并做出相应的行为。

另一种方法是创建一个假迭代器:

template <class T, class U>
struct ConvertIterator 
    typedef T dest_type;
    typedef U it_type;

    ConvertIterator(U&& val) : iterator(std::forward<U>(val)) 

    

    bool operator == (const ConvertIterator &other) const 
        return iterator == other.iterator;
    

    bool operator != (const ConvertIterator &other) const 
        return iterator != other.iterator;
    

    dest_type operator * () const 
        return convert<dest_type>(*iterator);
    

    ConvertIterator<T, U> & operator ++() 
        ++iterator;
        return *this;
    

    it_type iterator;
;

然后:

template<class ToType, class FromType>
ToType convert(const FromType& from)

    typedef ConvertIterator<typename ToType::value_type, decltype(from.begin()) > convert_it;

    return ToType(convert_it(from.begin()), convert_it(from.end()));

【讨论】:

注意:引用答案时,最好直接链接答案,或者至少给出回答者的姓名作为参考点,因为排名是动态的并且可能会发生变化。我将编辑您的答案以直接指向 Xeo 的答案(使用底部的“共享”链接),因为它目前是第三个,如果这不是您想要指向的答案......那么您将不得不修复链接。 我想将一个转换器(无状态?)模板函数对象传递给ConvertIterator,但这很烦人。唔。上面是不是遇到了递归调用convert没有终止大小写的问题? @Yakk:我认为您可以将第三个模板参数添加到ConvertIterator,并将其作为构造函数的第二个参数传递(对于开始和结束迭代器)。 typedef ConvertIterator&lt;..., ..., decltype(transformer)&gt; convert_it;convert_it(from.begin(), transformer)。对于您的第二个问题,不,一个转换函数(显示的那个)有两个参数,暗示还有另一个接受一个参数并且是非递归的来转换 value_type。 显示的转换函数有一个参数和两个类型参数。它与 convert&lt;ToType&gt;(*it) 完美匹配,没有 SFINAE(但正文无法编译)。我会删除T 并用F 替换它,让operator* 返回f(*iterator) - 重要的部分不是转换为的类型,而是进行转换的函数(对象)? 我不好,两种类型的参数,而不是一种。在我的示例中,只需声明另一个只有一个类型参数的 convert 函数,并让它做你想做的事,它编译。你的做事方式也有效(并且比我在前面评论中使用 3 个类型参数的方式更好),在 lambda 的情况下使用 Fdecltype(f) 是可以的。【参考方案2】:

这是一个基于函数的转换迭代器。它具有前向迭代器的所有正确类型定义。如果我们选择,我们可以升级它以支持传入 Base 迭代器类型的所有标记属性:

template<
  class Base,
  class F,
  class R=typename std::result_of<F(decltype(*std::declval<Base const&>()))>::type
>
struct convert_iterator:
  std::iterator<std::forward_iterator_tag,typename std::decay<R>::type>

  Base it;
  F f;

  template<class It, class Func>
  convert_iterator(It&&base, Func&&func):it(std::forward<It>(base)),
  // defaulted stuff:
  convert_iterator()=default;
  convert_iterator(convert_iterator const&)=default;
  convert_iterator(convert_iterator &&)=default;
  convert_iterator& operator=(convert_iterator const&)=default;
  convert_iterator& operator=(convert_iterator &&)=default;

  bool operator==(convert_iterator const&other) const 
    return it == other.it;
  
  bool operator!=(convert_iterator const&other) const  return !(*this==other); 

  // a bit overkill, but rvalue and lvalue overrides for these:
  R operator*() const& 
    return f(*it);
  
  R operator*() & 
    return f(*it);
  
  R operator*() const&& 
    return std::move(f)(*std::move(it));
  
  R operator*() && 
    return std::move(f)(*std::move(it));
  
  // normal pre-increment:
  convert_iterator& operator++()& 
    ++it;
    return *this;
  
  // pre-increment when we are guaranteed not to be used again can be done differently:
  convert_iterator operator++()&& 
    return std::next(std::move(it)), std::forward<F>(f);
  
  // block rvalue post-increment like a boss:
  convert_iterator operator++(int)& 
    return it++, f;
  
;

创建它们的辅助函数:

template< class Base, class F >
convert_iterator<typename std::decay<Base>::type,typename std::decay<F>::type>
make_convert_iterator(Base&& b, F&& f)  return std::forward<Base>(b), std::forward<F>(f); 

接下来我创建一个处理转换的类。专业化让我们可以对容器和标量进行不同的调度:

// for scalars:
template<class ToType,class=void>
struct converter 
  template<class FromType>
  ToType operator()(FromType&& from)const return std::forward<FromType>(from); 
;

// attempt at SFINAE test for container:
template<class ToContainer>
struct converter<ToContainer, (void)(
  typename std::iterator_traits<
    typename std::decay<decltype(std::begin(std::declval<ToContainer&>())>::type
  >::value_type
)>

  using std::begin; using std::end;

  using R=std::iterator_traits<typename std::decay<decltype(begin(std::declval<ToContainer&>()))>::type>::value_type;

  template<class FromType, class T=decltype(*begin(std::declval<FromType>())>
  ToContainer operator()(FromType&& from) const 
    auto sub_convert = [](T&& t)->R
      return converter<R>(std::forward<T>(t));
    ;
    return 
      make_convert_iterator(begin(std::forward<From>(from)), sub_convert),
      make_convert_iterator(end(std::forward<From>(from)), sub_convert)
    ;
  ;
;

动作转换函数现在是单行的:

template<class ToType>
ToType convert(FromType&& from)

  return converter<ToType>(std::forward<FromType>(from));

【讨论】:

以上是关于std::transform 到任意容器的主要内容,如果未能解决你的问题,请参考以下文章

git 回滚到任意版本

无缝音频循环到任意位置

python 计算π(pi)到任意的deptth

将404错误页重定向到任意页

R语言plyr包round_any函数将向量数据近似到任意精度实战

使用 Rcpp 将矩阵四舍五入到任意精度