【问题标题】:C++ implementation of ORDER BY on struct结构上 ORDER BY 的 C++ 实现
【发布时间】:2013-09-13 08:01:38
【问题描述】:

我在这里和其他网站上搜索了很多,但我没有找到令人满意的东西。

我需要的是非常简单的任务——基本上是在 C++ 中构造 ORDER BY 运算符。这意味着我有许多不同数据类型成员的结构,我需要一个比较器,它的成员和排序是可配置的。这是我的伪代码想法:

comparator.add(&MyStruct::member1, std::less);
comparator.add(&MyStruct::member2, std::greater);
std::sort(my_vector.begin(), my_vector.end(), comparator);

我得到按 member1 排序的数据,如果相等,则由 member2 决定,依此类推。

我不太擅长 stl 和模板,但我可以阅读和破译一些代码,发现这是非常合适的解决方案:https://stackoverflow.com/a/11167563

不幸的是,在我的工作中,我必须使用带有错误的 32 位编译器的 c++ builder 拒绝编译这个正确的代码。 support almost nothing from c++11,它有 boost 1.39 可用。

在我的可用资源的情况下,是否有人可以为我提供任何解决方案?提前谢谢你

编辑:

我得到了非常专业的解决方案,其中包含我知道的硬写比较运算符,但在这里不太好用。我在我的问题中错过了这个。我的结构至少有 15 个成员,正如我所写,我需要经常更改成员/列的单独排序方向(asc,desc)。同样,我也需要经常更改排序成员的集合,例如按 sql 中的操作符排序。我也不能使用 stable_sort 之类的东西,因为我只是为某个类的 OnCompare 事件编写比较器。

【问题讨论】:

标签: c++ sorting boost stl compare


【解决方案1】:

这并不难。首先,考虑“规范” 排序关系:

struct Compare
{
    bool operator()( C const& lhs, C const& rhs ) const
    {
        return lhs.a < rhs.a
            || ( !(rhs.a < lhs.a) && lsh.b < rhs.b )
            || ( !(rhs.a < lhs.a) && !(rhs.b < lhs.b) && lhs.c < rhs .c )
            || ...
    }
};

显然,没有人会真正写出这样的东西,但是 它完全对应于什么是的正式定义 需要。

当然,如果我们可以把数据成员想象成一个数组,我们 可以将其重写为循环,利用之前的 在每种情况下都建立!(rhs[i-1] &lt; lsh[i-1]

struct Compare
{
    bool operator()( C const& lhs, C const& rhs ) const
    {
        int i = 0;
        while ( i != N && !(lhs[i] < rhs[i]) && !(rhs[i] < lhs[i]) ) {
            ++ i;
        }
        return i != N && lhs[i] < rhs[i];
    }
};

或者,如果所有元素都是完全有序的,那么== 是 也定义了它们,我们可以假设它对应于 弱偏建立的等价关系 订购:

struct Compare
{
    bool operator()( C const& lhs, C const& rhs ) const
    {
        int i = 0;
        while ( i != N && !(lhs[i] == rhs[i]) ) {
            ++ i;
        }
        return i != N && lhs[i] < rhs[i];
    }
};

剩下的就是以某种方式将其转化为某种东西 可以处理任意元素的任意顺序 类型。有句老话,解决所有问题 是一个额外的间接级别,它适用于这里。

首先,我们需要一些方法来处理不同类型的 每个元素。多态性似乎是合适的(尽管 模板可以按照 if 的顺序工作 被评估的元素在编译时是固定的):

struct CompareOneElementOfC
{
    virtual bool isLessThan( C const& lhs, C const& rhs) const = 0;
    virtual bool isEqual( C const& lhs, C const& rhs) const = 0;
};

template <typename T, T C::*ptr>
struct ConcreteCompareOneElementOfC : public CompareOneElementOfC
{
    virtual bool isLessThan( C const& lhs, C const& rhs) const
    {
        return lhs.*ptr < rhs.*ptr;
    }
    virtual bool isEqual( C const& lhs, C const& rhs) const
    {
        return lhs.*ptr == rhs.*ptr;
    }
};

根据元素的类型,您可能需要手动 写具体的具体事例。如果任何元素 不支持总排序,你将不得不省略 isEqual,并相应修改以下代码。

到目前为止,我们只需要一个静态实例 具体比较:

ConcreteCompareOneElementOfC<int, &C::a> const c1;
ConcreteCompareOneElementOfC<double, &C::b> const c2;
//  ...

最后,将这些实例的地址放在一个表中:

CompareOneElementOfC const* const cmp[] = { &c1, &c2 ... };

您可以为不同的订单设置不同的表格。如果有 只有少数,为每个定义静态表,并完成 它。如果排序可以是任意的,请在 在每个排序之前按照所需的顺序飞行。

最后:

class Compare
{
    CompareOneElementOfC const* const* begin;
    CompareOneElementOfC const* const* end;
public:
    template< size_t N >
    Compare( CompareOneElementOfC const* const (&cmp)[N] )
        : begin( cmp )
        , end( cmp + N )
    {
    }
    bool
    operator()( C const& lhs, C const& rhs ) const
    {
        auto current = begin;
        while ( current != end && (*current)->isEqual( lhs, rhs ) ) {
            ++ current;
        }
        return current != end && (*current)->isLessThan( lhs, rhs );
    }
}

(请注意,我还没有实际测试过这段代码,所以有 可能是拼写错误和其他错误。还是基本思路 应该在那里。)

【讨论】:

  • 谢谢。这是一个很好的解决方案,并且有效。但是,它能否确定某个地方(在模板中或作为参数)还有比较功能?例如,我在 delphi CompareText 中有字符串,其他类型有 std::greater 等。
  • 对不起,我试着自己想 :) 我认为这应该能够使用简单的重载函数,如果我错了,请纠正我
  • @blizzard 如果你实现了只使用less的表单,你可以用简单的函数替换函数对象,而不是函数对象;函数对象的优点是它支持两个独立的函数,一个用于排序,另一个用于相等。 (另一个可能的优势是它可以保存以某种方式控制比较的额外数据。这通常不是那么有用,但在某些情况下可能有用。)
  • @blizzard 当然,使用额外的参数或数据成员扩展模板,或者(正如我提到的)针对特定情况使用手写比较是没有问题的。该模板更多的是作为一个示例,而不是一个独特的解决方案。
  • 谢谢,我已经做到了(扩展排序方向等等)。
【解决方案2】:

我认为重载 operator &lt; 对你有用。

struct Struct {
    int member1;
    int member2;

    bool operator<(const Struct& rhs) const {
        if (member1 != rhs.member1)
            return member1 < rhs.member1
        else
            return member2 > rhs.member2
    }
};

这样,每当比较Struct 的任意2 个实例时,它们都将通过operator &lt; 中定义的比较函数进行比较。

所以一个简单的std::sort(vec.begin(), vec.end()) 就可以了!

编辑:

否则你总是可以定义一个仿函数来比较每个元素。这只是一个带有重载operator () 的类,用于比较。

class ComparisonClass {
public:
    bool operator()(const Struct& lhs, const Struct& rhs) {
        if (lhs.member1 != rhs.member1)
            return lhs.member1 < rhs.member1
        else
            return lhs.member2 > rhs.member2
    }
};

您还可以定义ComparisonClass 的一些成员值,它们定义了比较的顺序。

使用它会像这样称呼它std::sort(vec.begin(), vec.end(), ComparisonClass());

EDIT2:

稍微复杂一点的代码 -

class ComparisonClass {
public:
    bool operator()(const Struct& lhs, const Struct& rhs) {
        for(int i=0; i<m_comparisonOrder.size(); i++) {
            int pos = m_comparisonOrder[i];
            if (lhs[pos] != rhs[pos]) {
                if (m_comparisonType[pos])
                    return lhs[pos] < rhs[pos];
                else
                    return lhs[pos] > rhs[pos];
            }
        }
    }

    std::vector<int> m_comparisonOrder.
    std::vector<bool> m_comparisonType;
};

这里我假设Struct 有一个operator [],它返回相应的成员变量。

【讨论】:

    【解决方案3】:

    为什么不提供一个专门的比较器函数,它首先检查member1,如果相等则检查member2

    喜欢

    bool comparator(const MyStruct& s1, const MyStruct& s2)
    {
        if (s1.member1 == s2.member1)
            return s1.member2 > s2.member2;
        else
            return s1.member1 < s2.member1;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多