参数化运算符重载

时间:2019-06-23 15:27:17

标签: c++ operator-overloading

让我们假设我们有一个类型X,我们想比较它的值。进一步假设,不仅存在一个比较,而且还有一系列比较,它们由另一种类型T的值来参数化。换句话说,假设我们获得了原型的功能:

bool f(T p, X v1, X v2);

给出参数p,如果f(p, v1, v2)按照与v1相对应的顺序比较小于v2,则p为真。

我现在正在寻找的一种方法是在给定类型为<的值p的词法范围内重载T运算符,使得v1 < v2为在该范围内本地编译为f(p, v1, v2)

仅出于说明目的,以下内容确实实现了我在Scheme中想要的功能,其中...表示有问题的词汇范围:

(let ((< (lambda (v1 v2) (f p v1 v2))))
  ...)

关于如何在C ++中获得几乎想要的东西,我有两个想法,但我对它们不满意。

第一个想法不是在类型X的值之间定义比较运算符,而是在类型std::pair <T, X>的值之间定义比较运算符。代替v1 < v2的人将不得不写std::pair (p, v1) < std::pair (p, v2),并且std::pair (p1, v2) < std::pair (p2, v2)被重载以编译为f(p1, v1, v2)。这种方法的问题是,例如,第二个参数p2完全多余。省略它会破坏对称性。

我的第二个想法是使用表达式模板之类的东西。在这里,v1 < v2不返回布尔值,而仅返回像p (v1 < v2)这样计算的抽象表达式(树),其中p ()被适当地重载。这种方法的问题在于无法重载bool (v1 < v2),尤其是v1 < v2 ? ... : ...之类的表达式将无法编译。

2 个答案:

答案 0 :(得分:1)

类似的事情会起作用:

#include <iostream>

struct PointA {
    int x, y;
    static bool(*compareLarger)(PointA const&, PointA const&);
    bool
    operator >(PointA const& rhs) const {
        return compareLarger(*this, rhs);
    }
};

bool(*PointA::compareLarger)(PointA const&, PointA const&) = nullptr;

bool
compareX(PointA const& lhs, PointA const& rhs) {
    return lhs.x > rhs.x;
}

bool
compareY(PointA const& lhs, PointA const& rhs) {
    return lhs.y > rhs.y;
}

int
main(int, char**) {
    PointA p1{0, 1}, p2{1, 0};
    PointA::compareLarger = compareX;
    if (p1 > p2) std::cout << "P1 is larger\n";
    else std::cout << "P2 is larger\n";
    PointA::compareLarger = compareY;
    if (p1 > p2) std::cout << "P1 is larger\n";
    else std::cout << "P2 is larger\n";
    return 0;
}

您在想什么?

编辑:

这很重要,甚至可以解决它的编译时间:

#include <iostream>

struct PointA {
    int x, y;
};

namespace NS1 {
    bool
    operator>(PointA const& lhs, PointA const& rhs) {
        return lhs.x > rhs.x;
    }
}

namespace NS2 {
    bool
    operator>(PointA const& lhs, PointA const& rhs) {
        return lhs.y > rhs.y;
    }
}

int
main(int, char**) {
    PointA p1{0, 1}, p2{1, 0};
    {
        using NS1::operator >;
        if (p1 > p2) std::cout << "P1 is larger\n";
        else std::cout << "P2 is larger\n";
    }
    {
        using NS2::operator >;
        if (p1 > p2) std::cout << "P1 is larger\n";
        else std::cout << "P2 is larger\n";
    }
    return 0;
}

答案 1 :(得分:1)

我真的不同意您的用例,但这仍然是一个有趣的问题。关于C ++,我最喜欢的事情是它如何赋予您如此多的控制权-实际上,以至于大多数其他语言的功能都可以通过聪明的C ++进行模拟。这种情况也不例外。

这是我一起解决的一个方案,应该与方案非常相似。它将在声明范围内更改用于特定类型和特定线程的比较器。我添加了评论来解释所有重要的内容。

#include <iostream>
#include <utility>
#include <cassert>

struct vector2
{
    int x, y;

    // the type of comparator to use (function pointer)
    typedef bool(*comparator_t)(const vector2&, const vector2&);
    // gets the value of comparator we're currently using by reference (one for each thread)
    static comparator_t &comparator()
    {
        thread_local comparator_t c = nullptr;
        return c;
    }

    // define comparison operator to use comparator()
    friend bool operator<(const vector2 &a, const vector2 &b) { return comparator()(a, b); }
};

// a sentry type for changing the comparator for type T in the current scope
// it changes it back upon destruction (at end of scope)
template<typename T>
struct comparator_sentry_t
{
    typename T::comparator_t old;
    explicit comparator_sentry_t(typename T::comparator_t c) : old(std::exchange(T::comparator(), c)) {}
    ~comparator_sentry_t() { T::comparator() = old; }
};

#define _MERGE(x, y) x ## y
#define MERGE(x, y) _MERGE(x, y)

// a user-level macro which is used to change the comparator for the rest of the current scope
#define SET_COMPARATOR(type, cmp) comparator_sentry_t<type> MERGE(__comparator_sentry, __LINE__) { (cmp) }

// -- below this line is demo code -- //

// a couple of example comparison functions
bool cmp_x_less(const vector2 &a, const vector2 &b) { return a.x < b.x; }
bool cmp_x_greater(const vector2 &a, const vector2 &b) { return a.x > b.x; }

bool cmp_y_less(const vector2 &a, const vector2 &b) { return a.y < b.y; }
bool cmp_y_greater(const vector2 &a, const vector2 &b) { return a.y > b.y; }

// some functions to demonstrate this works across function invocations
void foo(const vector2 &a, const vector2 &b)
{
    assert(a < b);
    SET_COMPARATOR(vector2, cmp_y_greater);
}
void bar(const vector2 &a, const vector2 &b)
{
    assert(b < a);
    SET_COMPARATOR(vector2, cmp_x_less);
}

int main()
{
    vector2 a{ 1, 3 };
    vector2 b{ 2, 6 };

    SET_COMPARATOR(vector2, cmp_x_less);
    SET_COMPARATOR(vector2, cmp_x_less); // redeclaring in same scope is ok

    assert(a < b);
    foo(a, b);     // changes comparator internally
    assert(a < b); // demonstrate that said change is reverted at end of function

    {
        // change comparator for this scope
        SET_COMPARATOR(vector2, cmp_y_greater);

        assert(b < a);
        bar(a, b);
        assert(b < a);
    }

    assert(a < b); // demonstrate the comparator change was reverted
    foo(a, b);
    assert(a < b);

    return 0;
}
相关问题