Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can overloading shift operators for things other than I/O be a good design?

I am implementing the Open List (OL) class for the A* search algorithm. The OL is basically a specialized priority queue of search nodes. It is common to see a notation like this in the pseudo-code describing the A* algorithm:

    successorNode -> OL // put the successor node into OL
    ...
    curNode <- OL // get the best node from OL and store it in curNode

Three questions:

  1. Would it make sense for my OL class to support a similar notation by overloading the shift operators:

    OL ol;
    ...  
    OL << successorNode;
    ... 
    OL >> curNode;
    
  2. (Only if the answer to 1. is "Yes") Can I go as far as to support this (i.e. the usage not supported by cout and cin for the built-in types):

    OL ol;
    ...
    successorNode >> OL;
    ...
    curNode << OL;
    
  3. (Only if the answer to 1. is "Yes") Would this usage of the shift operators make sense for standard containers:

    vector<int> v;
    v << 5; // instead of v.push_back(5)
    

EDIT: The purpose of this question is two-fold:

  • to ask whether the proposed design goes against the principle that overloaded operators should mimic the meaning of these operators for the built-in/standard types.

  • to ask why shift operators aren't used to allow less verbose usage of the standard containers.

like image 982
AlwaysLearning Avatar asked Jul 24 '15 08:07

AlwaysLearning


People also ask

Which of the following statements regarding operator overloading is not true?

Which of the following statements is NOT valid about operator overloading? Explanation: The overloaded operator must not have at least one operand of its class type.

Which of the following is true about operator overloading?

9. Which is the correct statement about operator overloading? Explanation: Both arithmetic and non-arithmetic operators can be overloaded. The precedence and associativity of operators remains the same after and before operator overloading.

What benefits does overloading an operator provide to a programmer?

Operator overloading in c++ enables programmers to use notation closer to the target domain. They provide similar support to built-in types of user-defined types. Operator overloading in c++ makes the program easier to understand.

Which of the following operator Cannot be overloaded?

7. Which of the following operator cannot be overloaded? Explanation: ?:, :: and . cannot be overloaded +, -, % can be overloaded.


2 Answers

The answers to your question are likely highly based on personal opinions, as there are no hard rules that allow/disallow this usage of operator overloading. So I will present arguments that should help you decide whether that is a good idea or not, instead of a hard answer.

Regarding your first two questions:

Think about it from the point of view of the principle of least surprise. If someone sees your code, what would he expect? Would it be immediately clear what is meant, or, as the opposite extreme, would (s)he expect something completely different? Is the overloading worth the suprise, if applicable? For example, does it make the code more clear after one has learned what the operators do? If the pros outweigh the cons, go for it! Otherwise, don't.

As a side node to that point, I have even come across the argument that the iostream operators are a bad example for operator overloading, since they don't shift integers. However, I tend to disagree, and see this as a matter of personal opinion.

Applied to your current situation: Might the user expect other results from calling the operator? E.g. might he expect another result from the queue? If so, don't overload. Or is the user expected to be familiar with the pseudo-code notation, and see the similarity? If so, do overload!

Regarding the third question:

Some people agree, some disagree. For example, the Qt framework's containers support that usage:

QList<int> list;
list<<5;

Summary:

The answer depends on whether it makes your code more readable (and, of course, personal opinion).

Note: All this applies only if there is no style guide or so that prohibits this use of operator overloading!

like image 178
anderas Avatar answered Oct 27 '22 01:10

anderas


It's a style question, so here are my 2 cents: I like to overload these operators where it helps to get a clean and concise syntax, and the usage is consistent with <iostream>. For instance:

void MyLoggableClass::foo(int i)
{
    LOG_TRACE("foo(" << i << ") called");
}

There is another macro that facilitates this usage and logs entry and exit, but you get the idea. Although I haven't used this for containers or container adapters, I guess it makes sense for "stream-like" containers/adapters, particularily FIFO queues, priority queues and even stacks. The Qt usage is a little more difficult to accept because you have to guess whether the new item is added at the front or back of the list. I assumed that it'd be the back, and the documentation confirms this, so it's not too surprising. However, they do not have an operator >> and that makes sense, because I would have no idea from which end of the list it would pop the item. That would be yes to question 1, and a "somewhat" to question 3.

Now for the second question, I would highly recommend not to do that. Why? While symmetry may seem nice, it's a lot more surprising to users of <iostream>, and even if we disregard that, it opens a can of worms:

queue<item> q;
item1 >> q << item2;

Really? Even if we could agree that the syntax was appropriate, a casual reader would now likely have to look up whether these operators are left- or right-associative, just to understand which item goes in the queue first (for a priority queue it would admittedly matter less). But the following is much worse. Imagine you have this code:

queue<int> q;
q << 3 << 50;

But then someone decides that he much prefers to have the values up front and rewrites it as:

queue<int> q;
50 >> 3 >> q;

Sanity has now finally left us – this will push a single value (6) to the queue, because it is evaluated as (50 >> 3) >> q (with an integer bitshift, resulting in the value 6). I think this would also be a strong reason why this usage was never considered, or maybe considered but dismissed, for <iostream>.

like image 32
Arne Vogel Avatar answered Oct 26 '22 23:10

Arne Vogel