让我们假设我们有一个类型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 ? ... : ...
之类的表达式将无法编译。
答案 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;
}