views:

49

answers:

3

I have a class with two definitions of ordering. (In the real problem, one is a total order and one is a semiorder.) But it's nice to be able to use the comparison operators rather than always having to use an explicit comparison function or functor object. So I figured I'd provide some comparison operators like this:

class C;

namespace Order1 {
  bool operator< (const C&, const C&);
}
namespace Order2 {
  bool operator< (const C&, const C&);
}

Operators for >, <=, >= are also defined of course, but that's not the point. Now a user can say using namespace Order1; or ... Order2 at file scope or block scope and get the requested behavior for the rest of that file/block.

The disappointing part, which I'd like to improve if possible, is that these usings can't nest.

void g(const C&, const C&);

using namespace Order1; // for most functions in this file

void f(const C& x, const C& y) {
  bool test1 = x < y; // Order1
  {
    // Would like to switch to Order2 for this block.
    using namespace Order2;
    bool test2 = x < y; // Ambiguous overload!

    g(x, y); // Unaffected by local using-s.
  }
}

Since using-directives don't hide anything when used in the same namespace, this doesn't provide a way to nicely temporarily reverse the meaning of the operators for a block scope.

Another related idea would be to allow dummy objects on the stack whose constructors and destructors manipulate the "current setting" for which behavior to use. But I don't think I want to head that way for this situation, since that would mean the equivalent of f above could change the behavior of other functions called like g.

Is there another way to get a similar effect but allowing nested operations with the innermost block "hiding" the others? Or am I stuck with one behavior of overloaded operators per declarative region? Which I guess is manageable, since code can still explicitly use a function or functor instead of using the operators.

A: 

instead of using namespace Order2 put in

bool test2 = Order2::operator<(x, y);

which will work in this instance. If you want to "meta-program", i.e. be able to pass in Order1 / Order2 as a template parameter you should make them structs (or classes) with operator< static. then you can do T::operator<(x, y) where T is either Order1 or Order2.

CashCow
If it's going to require a more specific syntax, I'd rather just go with `Order2::compare(x,y) < 0`, or the functor `Order2::Comp()` which inherits `std::binary_function<C,C,bool>`. I guess I was just trying to be too lazy for my own good.
aschepler
If you use a class you can overload operator(). You need to create a "dummy" instance of the class. Then you use compareObj(x,y).
CashCow
A: 

Why don't you scrap the namespaces, and just declare two overloads of operator < with different argument types, one for each class type?

UPDATE

As dgnorton has pointed out in a comment below, you have one class, with two different orderings defined on it. This does seem like a possible cause of problems. Is it possible to split this into two different classes?

Oli Charlesworth
In his case the class type is the same...he wants different behavior for the same operator...which probably isn't a good idea.
dgnorton
It may be a good idea if he is going to pass the predicate into an algorithm or a a std::set or similar to give different ordering. If he puts the maps into each namespace it might pick up the operator from that namespace automatically, although Koenig Lookup suggests it might look to the namespace of the class itself first.
CashCow
+2  A: 

I would stick with ordinary comparison functions. The rest of the code will be cleaner. No using namespace... or explicit calls to scoped operator<. Reads easier this way, IMO...

int main() {
   bool b = compare1(4, 5);
   b = compare2(4, 5);
}
dgnorton