C++严格弱序派生类

C++ strict weak ordering derived class

本文关键字:派生 C++      更新时间:2023-10-16

我试图在一个子类中实现严格的弱排序,我想把它放在STL集容器中。STL集合使用运算符<以严格的弱排序对其元素进行排序。在我的例子中,我有一个类型层次结构,我不太确定如何为派生类型实现这一点。为此,我制作了一个快速的现场演示,展示了我不确定的地方。我使用一种易于使用的std::tie技术对字段进行排序。我不确定的领域是我应该如何调用超类的运算符<在对派生字段调用std::tie比较之前。

struct Base {
    Base(const int& rIntVal,  const std::string& rStrVal)
        : mIntVal(rIntVal)
        , mStrVal(rStrVal)
    {}
    inline bool operator<(const Base& rhs) const {
        return std::tie(mIntVal, mStrVal) < std::tie(rhs.mIntVal, rhs.mStrVal);
    }
private:    
    int mIntVal;
    std::string mStrVal;
};
struct Derived : public Base {
    Derived(
        const int& rIntVal, 
        const std::string& rStrVal, 
        const std::string& rOtherStrVal,
        const std::string& rOtherStrVal1)
        : Base(rIntVal, rStrVal)
        , mOtherStrVal(rOtherStrVal)
        , mOtherStrVal1(rOtherStrVal1)
    {}
    inline bool operator<(const Derived& rhs) const {
        // not sure what to do here - this is my best guess???
        if( Base::operator<(rhs) ) {
            return std::tie(mOtherStrVal, mOtherStrVal1) < 
                std::tie(rhs.mOtherStrVal, rhs.mOtherStrVal1);
        } else {
            return false;
        }
    }
private:    
    std::string mOtherStrVal;
    std::string mOtherStrVal1;
};

最好将引用绑定到基类:

bool operator<(const Derived& rhs) const {
    return std::tie(static_cast<const Base&>(*this), mOtherStrVal, mOtherStrVal1) < 
        std::tie(static_cast<const Base&>(rhs), rhs.mOtherStrVal, rhs.mOtherStrVal1);
}

这将首先按超类字段进行比较,然后按子类字段进行对比。

首先,您可以选择让派生字段优先于基本字段,以便首先考虑派生成员,或者您可以给基本字段优先权。该怎么做取决于你的类的含义以及它应该如何排序。

您已经选择先比较基本字段,这很好,所以我们将继续。

要成为一个严格的弱排序,您应该只在基子对象相等(即两者都不小于另一个(时比较派生字段。

对于您当前的代码,如果您有lhs.mIntVal < rhs.mIntVal,则应该返回true,但您继续比较派生字段,并可能最终说lhs小于rhs,而不是,即使基类的结果表明是。

因此,为了使结果正确,你需要一些等效的东西:

bool operator<(const Derived& rhs) const {
    if (Base::operator<(rhs))
      return true;
    else if (rhs.Base::operator<(*this))
      return false;
    // base parts are equivalent, compare derived parts:
    return std::tie(mOtherStrVal, mOtherStrVal1) < 
           std::tie(rhs.mOtherStrVal, rhs.mOtherStrVal1);
}

这在逻辑上是正确的,但次优,因为您调用了Base::operator<两次。如ecatmur所示,您可以通过在tie表达式中包含基础对象来避免这种情况。