Here is some code I'm writing in C++. There's a call to an addAVP() function
dMessage.addAVP(AVP_DESTINATION_HOST, peer->getDestinationHost() || peer->getHost());
which has two versions: one overloaded in the second parameter to addAVP(int, char*)
and another to addAVP(int, int)
. I find the C++ compiler I use calls the addAVP(int, int)
version which is not what I wanted since getDestinationHost()
and getHost()
both return char*
.
Nonetheless the || operator is defined to return bool so I can see where my error is. Bool somehow counts as an integer and this compiles cleanly and calls the second addAVP()
.
Lately I'm using a lot of dynamically typed languages, i.e. lisp, where the above code is correct can be written without worries. Clearly, clearly the above code in C++ is a big error, but still have some questions:
-
Should I be using this kind of shortcut, i.e. using the ||-operator's return value, at all in C++. Is this compiler dependent?
-
Imagine that I really, really had to write the nice
a || b
syntax, could this be done cleanly in C++? By writing an operator redefinition? Without losing performance?
As a followup to my original request, or my own answer to 2 🙂 I was thinking along the lines of using a class to encapsulate the (evil?) rawpointer:
class char_ptr_w { const char* wrapped_; public: char_ptr_w(const char* wrapped) : wrapped_(wrapped) {} char_ptr_w(char_ptr_w const& orig) { wrapped_=orig.wrapped(); } ~char_ptr_w() {} inline const char* wrapped() const { return wrapped_; } }; inline char_ptr_w operator||(char_ptr_w &lhs, char_ptr_w& rhs) { if (lhs.wrapped() != NULL) return char_ptr_w(lhs.wrapped()); else return char_ptr_w(rhs.wrapped()); };
Then I could use:
char_ptr_w a(getDestinationHost()); char_ptr_w b(getHost()); addAVP(AVP_DESTINATION_HOST, a || b);
In which this addAVP
would be overloaded for char_ptr_w
. According to my tests, this generates at most the same assembly code as ternary a?b:c
solution, particularly because of the NRVO optimization in the operator, which does not, in most compilers, call the copy-constructor (although you have to include it).
Naturally, in this particular example I agree that the ternary solution is the best. I also agree that operator redefinition is something to be taken with care, and not always beneficial. But is there anything conceptually wrong, in a C++ sense, with the above solution?
Best Answer
It is legal in C++ to overload the logic operators, but only if one or both of the arguments are of a class type, and anyway it's a very bad idea. Overloaded logic operators do not short circuit, so this may cause apparently valid code elsewhere in your program to crash.