简体   繁体   中英

C++ logical operators return value

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, ie 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:

  1. Should I be using this kind of shortcut, ie using the ||-operator's return value, at all in C++. Is this compiler dependent?

  2. Imagine that I really, really had to write the nice a || b 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?

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.

return p && p->q;  // this can't possibly dereference a null pointer... can it?

As you discovered, a bool is really an int. The compiler is picking the correct function for your footprint. If you want to keep similar syntax, you might try

char*gdh=0;
dMessage.addAVP(AVP\_DESTINATION\_HOST, 
                (gdh=peer->getDestinationHost()) ? gdh : peer->getHost());

I would strongly recommend against redefining the operator. From a maintenance perspective, this is very likely to confuse later developers.

Why are you using an "or" operator on two char pointers?

I am assuming that peer->getDestinationHost() or peer->getHost() can return a NULL, and you are trying to use the one that returns a valid string, right?

In that case you need to do this separately:


char *host = peer->getDestinationHost();
if(host == NULL)
  host = peer->getHost();
dMessage.addAVP(AVP\_DESTINATION\_HOST, host);

It makes no sense to pass a boolean to a function that expects a char *.

In C++ || returns a bool, not one of its operands. It is usually a bad idea to fight the language.

1) Should I be using this kind of shortcut, ie using the ||-operator's return value, at all in C++. Is this compiler dependent?

It's not compiler dependent, but it doesn't do the same as what the || operator does in languages such as JavaScript or or in common lisp. It coerces it first operand to a boolean values, and if that operand is true returns true. If the first operand is false, the second is evaluated and coerced to a boolean value, and this boolean value is returned.

So what it is doing is the same as ( peer->getDestinationHost() != 0 ) || ( peer->getHost() != 0 ) ( peer->getDestinationHost() != 0 ) || ( peer->getHost() != 0 ) . This behaviour is not compiler dependent.

2) 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?

Since you are using pointers to chars, you can't overload the operator ( overloading requires one formal parameter of a class type, and you've got two pointers ). The equivalent statement C++ would be to store the first value in a temporary variable and then use the ?: ternary operator, or you can write it inline with the cost of evaluating the first expression twice.

You could instead do something like:

dMessage.addAVP(AVP_DESTINATION_HOST, (peer->getDestinationHost())? peer->getDestinationHost() : peer->getHost());

This is not as neat as || but near to it.

Well, you're right about what the problem is with your code: a || b will return a bool, which is converted to int (0 for false, != 0 for true).

As for your questions:

  1. I'm not sure whether the return value is actually defined in the standard or not, but I wouldn't use the return value of || in any context other than a bool (since it's just not going to be clear).

  2. I would use the ? operator instead. The syntax is: (Expression) ? (execute if true) : (execute if false). So in your case, I'd write: (peer->getDestinationHost() =! NULL) ? peer->getDestinationHost() : peer->getHost(). Of course, this will call getDestinationHost() twice, which might not be desirable. If it's not, you're going to have to save the return value of getDestinationHost(), in which case I'd just forget about making it short and neat, and just use a plain old "if" outside of the function call. That's the best way to keep it working, efficient, and most importantly, readable .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM