boost是否具有比STL更简单的set操作的数据类型?

Tim*_* MB 26 c++ boost stl set

我发现使用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;
Run Code Online (Sandbox Code Playgroud)

boost是否提供了一组替代类,可以将上面的示例缩小为:

set_type<int> newUserIds = currentUserIds.difference(mPreviousUserIds);
set_type<int> missingUserIds = mPreviousUserIds.difference(currentUserIds);
Run Code Online (Sandbox Code Playgroud)

(类似于Qt中的QSet,它operator-以这种方式覆盖.)

Yak*_*ont 83

不.但我在这里是如何清理它.

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

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

第三,可能太过分了,把它们写成命名运算符.

最终结果是:

set<int> s = a *intersect* b;
set<int> s2 = c -difference- s;
set<int> s3 = a *_union_* (b *intersect* s -difference- s2);
Run Code Online (Sandbox Code Playgroud)

...在其他地方写了一大堆样板代码之后.

据我所知,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_) {}
};
Run Code Online (Sandbox Code Playgroud)

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

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

container_builder然后可以隐式地转换为接受std::back_inserter(或具有自己的ADL back_inserter)的任何容器,并且move每个std容器上的语义使得construct-then-return非常有效.

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

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) );
  }
}
Run Code Online (Sandbox Code Playgroud)

用它来实现的实例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;
Run Code Online (Sandbox Code Playgroud)

实例.

  • 哇.命名运营商.心神.吹.为什么我以前从没想过这个? (27认同)
  • @KonradRudolph C++中命名运算符的优先级与"around"运算符相同.因此,'%op%`与优先级中的其他乘法/除法运算符相关联,并且`<op>`比除赋值,逻辑和`==`类型操作之外的任何东西都宽松.`v1*dot*v2 + v3`遵循预期的优先规则,`v0*dot*v1 <cmp> v2*dot*v3`也是如此.鉴于任何二元运算符都有效,并且它有意义,我只是让用户选择 - 所以`make_infix <'%','<>'>(func)`为你提供语法.一个很好的副作用是`vec1 + append = vec2;`语法也很好看. (5认同)
  • @KonradRudolph这里有一个[实现](http://liveworkspace.org/code/12lxjX$40).`make_infix <'*'>(arbitrary_binary_functor)`在`*`上返回一个命名运算符.(`make_infix`下面的所有内容都是各种测试代码) (2认同)
  • @noɥʇʎԀʎzɐɹƆ获得更好的静态代码分析器。或者只是在使用命名操作符命名空间之前写成template &lt;class ... Ts&gt; void named_invoke(Ts &amp;&amp; ...)= delete;`,这可能会关闭它。也许不会; 确实可以解决分析器中的错误,所以谁知道我需要做什么。 (2认同)
  • @noɥʇʎԀʎzɐɹƆ[在零时间内查看10 ^ 18次迭代](http://coliru.stacked-crooked.com/a/3bc042c3d3b5ed00)。如果您需要帮助来分析可以编译成零的东西,请询问有关它的SO问题。没有要分析的内容,优化的编译器的输出中不存在指定的运算符代码。我敢肯定有些编译器无法优化它,或者无法完成它,但这是特定编译器中实现质量的问题。 (2认同)

Max*_*kin 11

请参阅Boost范围集算法.他们仍然期望输出迭代器.

  • 一个例子会更好.-1 (2认同)