假设我们有以下代码段:
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
情况可能更复杂,衍生的类比2更多。当然,operator<
必须是虚函数。是否有一种优雅的方式来写这个?
答案 0 :(得分:2)
我看到的唯一解决方案是不将operator<
函数作为虚拟成员函数,而是作为一组重载的非成员函数:One&#34; default&#34;函数,它将A
的两个引用作为参数,然后为特殊情况分别进行一次重载。
答案 1 :(得分:2)
///
/// 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());
}
示例输出(clang):
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&lt; y由特定函数const运算符&lt;给出。 B类中的(B&amp; rhs)const。它不一定是假的。
好的,我们正在修改要求。这是用户之间的常规对话(他们很少意识到规范中要求的详细程度)和开发人员(谁做!)
所以这次我们会说任何两个不同的派生类都会有一致的部分排序(即它们永远不会比较相等,而且总是会比另一个更少)但我们会让标准库决定哪一个来第一
但是,当比较的两个类属于同一类型时,我们希望实际比较它们的值以确定排序(和等价)。
它会是这样的:
#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"