比较运算符的虚拟重载



>假设我们有以下代码片段:

class A
{
public:
    virtual bool operator< (const A &rhs) const;
};
class B: public A;
class C: public A;

我希望比较取决于左侧和右侧的实际类型,例如:

x < y == true    if type(x) == B and type(y) == C
x < y == false   if type(x) == C and type(y) == B

情况可能更复杂,派生类比两个多得多。当然,operator<必须是一个虚拟功能。有没有一种优雅的写法?

///
/// goal:       provide partial ordering of objects derived from A on the basis
///             only of class type.
/// constraint: ordering specified by us
///
#include <vector>
#include <typeindex>
#include <algorithm>
#include <iostream>

class A
{
public:
    virtual bool operator< (const A &rhs) const = 0;
    static const std::vector<std::type_index>& ordering();
};
template<class T> struct impl_A : public A
{
    bool operator< (const A &rhs) const override
    {
        auto& o = ordering();
        auto first = std::begin(o);
        auto last = std::end(o);
        auto il = std::find(first, last, typeid(T));
        auto ir = std::find(first, last, typeid(rhs));
        return il < ir;
    }
};
class B: public impl_A<B> {};
class C: public impl_A<C> {};
const std::vector<std::type_index>& A::ordering()
{
    // specify fording of types explicitly
    static const std::vector<std::type_index> _ordering { typeid(B), typeid(C) };
    return _ordering;
}

void test(const A& l, const A& r)
{
    if (l < r) {
        std::cout << typeid(l).name() << " is less than " << typeid(r).name() << std::endl;
    }
    else {
        std::cout << typeid(l).name() << " is not less than " << typeid(r).name() << std::endl;
    }
}
int main()
{
    test(B(), C());
    test(B(), B());
    test(C(), B());
    test(C(), C());
}

示例输出(喇叭):

1B is less than 1C
1B is not less than 1B
1C is not less than 1B
1C is not less than 1C

好!但是(我的问题不够精确),当 x 和 y 共享相同的类型(例如 B)时,x

好的,我们正在修改要求。这是用户(他们很少意识到规范中所需的详细程度)和开发人员(谁意识到!

所以这次我们要说任何两个不同的派生类将具有一致的偏序(即它们永远不会比较相等,一个总是比另一个比较少),但我们将让标准库决定哪一个先来。

但是,当被比较的两个类属于同一类型时,我们希望实际比较它们的值以确定排序(和等价性)。

它会是这样的:

#include <vector>
#include <typeinfo>
#include <algorithm>
#include <iostream>
#include <tuple>
#include <iomanip>

class A
{
public:
    virtual bool operator< (const A &rhs) const = 0;
    std::ostream& print(std::ostream& os) const {
        handle_print(os);
        return os;
    }
private:
    virtual void handle_print(std::ostream&) const = 0;
};
std::ostream& operator<<(std::ostream& os, const A& a) {
    return a.print(os);
}
template<class T> struct impl_A : public A
{
    bool operator< (const A &rhs) const override
    {
        auto& rhs_info = typeid(rhs);
        auto& lhs_info = typeid(T);
        if (rhs_info == lhs_info) {
            // same type, so do comparison
            return static_cast<const T&>(*this).ordering_tuple() < static_cast<const T&>(rhs).ordering_tuple();
        }
        else {
            return lhs_info.before(rhs_info);
        }
    }
};
class B: public impl_A<B> {
public:
    B(int v) : _value(v) {}
    auto ordering_tuple() const {
        return std::tie(_value);
    }
private:
    void handle_print(std::ostream& os) const override {
        os << _value;
    }
    int _value;
};

class C: public impl_A<C> {
public:
    C(std::string v) : _value(std::move(v)) {}
    auto ordering_tuple() const {
        return std::tie(_value);
    }
private:
    void handle_print(std::ostream& os) const override {
        os << std::quoted(_value);
    }
    std::string _value;
};
// now we need to write some compare functions

void test(const A& l, const A& r)
{
    if (l < r) {
        std::cout << l << " is less than " << r << std::endl;
    }
    else {
        std::cout << l << " is not less than " << r << std::endl;
    }
}
int main()
{
    test(B(1), C("hello"));
    test(B(0), B(1));
    test(B(1), B(0));
    test(B(0), B(0));
    test(C("hello"), B(1));
    test(C("goodbye"), C("hello"));
    test(C("goodbye"), C("goodbye"));
    test(C("hello"), C("goodbye"));
}

示例结果:

1 is less than "hello"
0 is less than 1
1 is not less than 0
0 is not less than 0
"hello" is not less than 1
"goodbye" is less than "hello"
"goodbye" is not less than "goodbye"
"hello" is not less than "goodbye"

我看到的唯一解决方案是不要将operator<函数作为虚拟成员函数,而是作为一组重载的非成员函数:一个"默认"函数,它将两个对A的引用作为参数,然后在特殊情况下各重载一个。

最新更新