I want to store a sequence of string in a queue. This seems pretty simple if i use the member function push()
queue test;
string s0(\"s0\"), s1(
While C++ doesn't allow you this, it allows you to do something very similar: test << s0 << s1;
However, don't do this!
If I see test.push(s0)
, I know exactly what it does, without even looking at the type of test
. If I see test << s0 << s1;
, I'd think test
is a stream that's written to.
Here's my three basic rules you should stick to when overloading operators:
+
operator to change its right operand. However, the users of such an operator would never suspect the expression a + b
to change the value of b
. This is why the second rule of operator overloading says: Always stick to the operator’s well-known semantics. (This, in turn, presumes that the semantics are undisputed; see the previous rule.) a + b
, users will expect to be able to call a += b
, too. If it supports prefix increment ++a, they will expect a++ to work, too. If they can check whether a < b
, they will most certainly expect to also to be able to check whether a > b
. If they can copy-construct your type, they expect assignment to work as well. So the third rule of operator overloading reminds you: Always provide all out of a set of related operations. As with all such rules, there are indeed exceptions. Sometimes people have deviated from them and the outcome was not bad code, but such deviations are few and far between. At the very least, 99 out of 100 such deviations I have seen were unjustified. However, it might just as well have been 999 out of 1000. So you’d better stick to these rules.
So just in case I hadn't been clear enough: For the code from your question, a deviation from these rules is very bad.
First off, your example of 'implicitly' enqueueing items has no mention of the queue - do you mean something like:
test << s0 << s1 << s2 << s3;
If so, it's possible, but I wouldn't recommend it. It really doesn't help readability that much. If you really do want it, though, put this in a header somewhere and include it wherever you want this behavior:
template<typename T>
std::queue<T> &operator<<(std::queue<T> &q, const T &v)
{
q.push(v);
return q;
}
Note that the opposite order - s0 >> s1 >> s2 >> test
- is not possible, due to C++ precedence rules.
I agreed with most answers, which told you that you could do it, as long as you included the queue object, and I also agree that readability is compromised since it's a very non-standard behavior.
Still, I was wondering. What if you try something like:
queue test;
string s0("s0"), s1("s1");
test.push(s0).push(s1);
That could be implemented very simply, would still give you the correct readability (since the meaning of push
is well understood), and keep your code concise (which seems to be your main objective).
To implement it, all you'd have to do is extend the Queue class (or write your own queue class which would, in turn, wrap around an STL Queue object).
Qt containers work exactly like this:
QStringList list;
list << "Sven" << "Kim" << "Ola";
QVector<QString> vect = list.toVector();
// vect: ["Sven", "Kim", "Ola"]
If you want the same to work with STL containers, you would need to write the operator overloading yourself but obviously you can't add operations to std namespace.
If you wanted to do this it would typically be expressed as
test << s0 << s1 << s2 << s3;
by suitable definition of overloaded operator<<
on the queue class. I don't know why this would be better than a simple series of test.push()
calls though.
I would not really do it, but if you really feel that you need to provide that syntax, you can write an inserter adapter...
template <typename C>
class inserter_type {
public:
typedef C container_type;
typedef typename container_type::value_type value_type;
explicit inserter_type( container_type & container ) : container(container) {}
inserter_type& operator<<( value_type const & value ) {
container.push( value );
return *this;
}
private:
container_type & container;
};
template <typename C>
inserter_type<C> inserter( C & container ) {
return inserter_type<C>(container);
}
int main() {
std::queue<std::string> q;
inserter(q) << "Hi" << "there";
}