【问题标题】:Does boost have a datatype for set operations that is simpler than the STL?boost 是否具有比 STL 更简单的集合操作数据类型?
【发布时间】:2013-02-11 23:14:34
【问题描述】:

我发现 the C++ STL method 进行简单的集合操作非常笨拙。例如,要找出两组之间的差异:

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 中的 QSet 以这种方式覆盖 operator-。)

【问题讨论】:

  • 如果你愿意,可以用五指练习来写。
  • 是的,但它给我留下了个人代码库,可以导入到我需要的任何项目中,这并不总是可能的(例如在工作中),并且让其他人更难理解代码。

标签: c++ boost stl set


【解决方案1】:

不。但我这里是如何清理它。

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

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

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

最终的结果是你得到:

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 容器上的move 语义使得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) );
  }
}

live example 使用它来实现vector *concat* vector。它只支持一个运算符,但扩展它很容易。对于严肃的使用,我建议有一个times 函数,默认情况下调用invoke*blah*,一个add+blah+ 执行相同的操作,等等。&lt;blah&gt; 可以直接调用invoke .

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

Here is a similar library being used to implement *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;

live example.

【讨论】:

  • 哇。命名运算符。头脑。吹。为什么我以前从来没有想过这个?
  • @KonradRudolph 在这里,有一个implementationmake_infix&lt;'*'&gt;(arbitrary_binary_functor)* 上返回一个命名运算符。 (make_infix下面都是各种测试代码)
  • @KonradRudolph C++ 中命名运算符的优先级与“周围”运算符相同。 %op% 因此优先与其他乘法/除法运算符联系在一起,并且&lt;op&gt; 比除赋值、逻辑和== 类型操作之外的任何操作都更宽松。 v1 *dot* v2 + v3 遵循预期的优先规则,v0 *dot* v1 &lt;cmp&gt; v2 *dot* v3 也是如此。鉴于任何二元运算符都有效且有意义,我只是让用户选择——所以make_infix&lt;'%','&lt;&gt;'&gt;(func) 为您提供了您的语法。一个很好的副作用是vec1 +append= vec2; 语法读起来也很漂亮。
  • @noɥʇʎԀʎzɐɹƆ 获得更好的静态代码分析器。或者在使用前在命名的操作符命名空间中写template&lt;class...Ts&gt;void named_invoke(Ts&amp;&amp;...)=delete;,这可能会关闭它。可能不会;确实在解决分析器中的错误,所以谁知道我需要做什么。
  • @noɥʇʎԀʎzɐɹƆ Look 10^18 iterations in zero time.如果您需要帮助分析一些编译为无的东西,请询问有关它的 SO 问题。没有什么可分析的,优化编译器的输出中不存在命名运算符代码。我确信有些编译器无法优化它或无法完成它,但这是该特定编译器的实现质量问题。
【解决方案2】:

Boost Range Set algorithms。他们仍然期望输出迭代器。

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 我认为 OP 的优势不在于非成员函数——成员函数实现只是一个示例。并且有一些简单(而且不是那么简单)的方法可以通过适当的库使 OP 的代码更加简洁,而无需使用成员函数(我相信 Boost.Range 确实提供了这种方法)。
    猜你喜欢
    • 2020-08-08
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 2015-09-01
    • 1970-01-01
    • 2011-12-04
    • 1970-01-01
    相关资源
    最近更新 更多