两种方法的推广

时间:2018-07-01 15:03:04

标签: c++ templates refactoring

考虑结构:

onFirst

是否可以将方法onSecondcompBLess重构为一个简单的方法而又不影响代码可维护性?请注意,不能使用compBGreater / comp代替 template<typename TSet> void onBoth(int val){ TSet* set; if ( std::is_same<TSet, decltype(m_setLess)>::value ) { set = reinterpret_cast<TSet*>(&m_setLess); } else { set = reinterpret_cast<TSet*>(&m_setGreater); } auto i_set = set->begin(); std::function<bool()> comp; if( std::is_same<TSet, decltype(m_setLess)>::value ) comp = std::function<bool()>([&]() { return val >= i_set->b1; }); else comp = std::function<bool()>([&]() { return val <= i_set->b1; }); while ( i_set != set->end() && comp() ) { sameForBoth(*i_set); ++i_set; } }

我对这个问题的看法:

reinterpret_cast<>

但是我的解决方案似乎太复杂了。另外,我不确定以这种方式使用replace()是否是一个好习惯。

还有其他方法吗?

2 个答案:

答案 0 :(得分:2)

如果我正确理解这一点,似乎您想对碰巧满足comp一元谓词的每个元素应用一个操作。因此,我们可以线性扫描元素并应用函数,直到给定谓词成立为止。

由于您正在研究范围,因此可能的方法是设计通用过程,如下所示:

template <typename I, typename P, typename F>
// I models InputIterator
// P models UnaryPredicate
// F models UnaryFunction
// Domain<P> == ValueType<I>
// Domain<F> == ValueType<I>
void apply_until(I first, I last, P p, F f) {
  while (first != last) {
    if (!p(*first)) return;
    f(*first);
    ++first;
  }
}

我们可以使用这种通用算法,例如打印出严格小于3的范围内的值:

int main() {
  std::set<int> s1 = {1, 2, 3, 4, 5};
  apply_until(s1.begin(), s1.end(), [](int x) { return x < 3;}, [](int x) { std::cout << x << '\n'; });
}

我会保留onFirstonSecond之间的区别,因为它们是要在不同的集合上运行。用例的代码可能减少为:

void onFirst(int val) {
    apply_until(m_setLess.begin(), m_setLess.end(), [&](B const& x) { return x.b1 >= val; }, [&](B const& x) { sameForBoth(x); });
}

void onSecond(int val) {
    apply_until(m_setGreater.begin(), m_setGreater.end(), [&](B const& x) { return x.b1 <= val; }, [&](B const& x) { sameForBoth(x); });
}

答案 1 :(得分:1)

在这两个函数中,我们都在一个范围内进行迭代,从std::set的开始一直到取决于输入值的给定迭代器。

在这个答案中,我假设compBLesscompBGreater的定义是这样的(重要的部分是b1字段比b2字段占主导地位。最后请看稍有不同的内容版本)

struct compBLess {
  bool operator ()(B const & o1, B const& o2) const {
      return std::make_pair(o1.b1,o1.b2) < std::make_pair(o2.b1,o2.b2);
  }
};
struct compBGreater {
  bool operator ()(B const & o1, B const& o2) const {
      return std::make_pair(o1.b1,o1.b2) > std::make_pair(o2.b1,o2.b2);
  }
};

在这种假设下,我认为惯用的方法是使用lowerbound, upperbound methods of std::set查找迭代的结束,然后使用

template<typename Iterator>
void foo(Iterator it, Iterator end) {
    std::for_each(it,end,[this](auto & o){ sameForBoth(o); });
}

这将在性能上更加有效,因为我们将进行O(log(size_of_set))比较(使用下限/上限),而不是O(size_of_set)比较(在循环中使用comp

其他方法的实际实现如下:

void onFirst(int val) {
    foo(m_setLess.begin(), m_setLess.lowerbound({val,std::numeric_limits<int>::min}));
}
void onSecond(int val) {
    foo(m_setGreater.begin(), m_setGreater.upperbound({val-1,std::numeric_limits<int>::max}));
}

编辑:在@ z3dd的评论之后,以下是适用于稍有不同的compBLesscompBGreater(与b2字段的顺序相反)的实现:

struct compBLess {
  bool operator ()(B const & o1, B const& o2) const {
      return std::make_pair(o1.b1,-o1.b2) < std::make_pair(o2.b1,-o2.b2);
  }
};
struct compBGreater {
  bool operator ()(B const & o1, B const& o2) const {
      return std::make_pair(o1.b1,-o1.b2) > std::make_pair(o2.b1,-o2.b2);
  }
};

void onFirst(int val) {
    foo(m_setLess.begin(), m_setLess.lowerbound({val,std::numeric_limits<int>::max}));
}
void onSecond(int val) {
    foo(m_setGreater.begin(), m_setGreater.upperbound({val-1,std::numeric_limits<int>::min}));
}

[请注意,如果compBLesscompBGreater的实现方式与给定的2种实现方式中的任何一种都不一样,则@Ilio Catallo的答案就是一种可以使用的方式。]