29

我发现执行简单集合操作的 C++ STL 方法使用起来非常笨拙。例如,要查找两组之间的差异:

std::set<int> newUserIds;
set_difference(currentUserIds.begin(), currentUserIds.end(), mPreviousUserIds.begin(), mPreviousUserIds.end(), std::inserter(newUserIds, newUserIds.end()));
std::set<int> missingUserIds;
set_difference(mPreviousUserIds.begin(), mPreviousUserIds.end(), currentUserIds.begin(), currentUserIds.end(), std::inserter(missingUserIds, missingUserIds.end()));
mPreviousUserIds = currentUserIds;

boost 是否提供了一组替代类,可以将上述示例简化为如下所示:

set_type<int> newUserIds = currentUserIds.difference(mPreviousUserIds);
set_type<int> missingUserIds = mPreviousUserIds.difference(currentUserIds);

(类似于Qt 中的QSetoperator- ,它以这种方式覆盖。)

4

3 回答 3

89

没有。但我这里是如何清理它。

首先,将基于迭代器的函数重写为基于范围的函数。这将您的样板减半。

其次,让它们返回容器构建器而不是使用插入迭代器:这为您提供了高效的赋值语法。

第三,可能太过分了,把它们写成命名操作符。

最后的结果是你得到:

set<int> s = a *intersect* b;
set<int> s2 = c -difference- s;
set<int> s3 = a *_union_* (b *intersect* s -difference- s2);

...在其他地方编写了大量样板代码之后。

据我所知,boost 会执行第 1 步。

但是上述三个阶段中的每一个都应该显着减少你的样板。

容器构建器:

template<typename Functor>
struct container_builder {
  Functor f;
  template<typename Container, typename=typename std::enable_if<back_insertable<Container>::value>::type>
  operator Container() const {
    Container retval;
    using std::back_inserter;
    f( back_inserter(retval) );
    return retval;
  }
  container_builder(Functor const& f_):f(f_) {}
};

这需要写作is_back_insertable(相当标准的 SFINAE)。

您包装基于范围(或基于迭代器)的函子,该函子将 back_insert_iterator 作为最后一个参数,并用于std::bind绑定输入参数,使最后一个空闲。然后将其传递给container_builder,然后返回。

container_builder然后可以隐式转换到任何接受std::back_inserter(或具有自己的 ADL back_inserter)的容器,并且move每个容器上的语义std使得构造-然后-返回非常有效。

这是我的十几行命名运算符库:

namespace named_operator {
  template<class D>struct make_operator{make_operator(){}};

  template<class T, char, class O> struct half_apply { T&& lhs; };

  template<class Lhs, class Op>
  half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
    return {std::forward<Lhs>(lhs)};
  }

  template<class Lhs, class Op, class Rhs>
  auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
  -> decltype( named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
  {
    return named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
  }
}

使用它来实现的现场示例vector *concat* vector。它只支持一个运算符,但扩展它很容易。对于严肃的使用,我建议有一个times默认调用的函数,invoke一个*blah*同样add的函数+blah+,等等。 <blah>可以直接调用invoke.

然后客户端程序员可以重载特定于运算符的重载并且它可以工作,或者一般的invoke.

这是一个类似的库,用于实现*then*元组返回函数和期货。

这是一个原语*in*

namespace my_op {
  struct in_t:named_operator::make_operator<in_t>{};
  in_t in;

  template<class E, class C>
  bool named_invoke( E const& e, in_t, C const& container ) {
    using std::begin; using std::end;
    return std::find( begin(container), end(container), e ) != end(container);
  }
}
using my_op::in;

活生生的例子

于 2013-02-26T13:47:20.073 回答
12

请参阅Boost Range Set 算法。不过,他们仍然期望输出迭代器。

于 2013-02-26T13:24:16.773 回答
3

不,我认为它从来没有这样的东西,这是 C++ 中的一般原则,即当你可以有一个非成员函数来完成这项工作时,永远不要让该函数成为成员。所以它不能那样,但可能是Boost::Range帮助你。

于 2013-02-26T13:25:27.087 回答