I oversimplified my example.
In fact, my C++ code doesn't listen to a single queue, but many. So that simple messaging piece is equivalent to either having many separate sockets open or multiplexing many communication channels over a single socket. I also don't need to worry about message boundaries, AMQP handles that for me. Instead of building a state machine with boost::asio that parses messages, I just call "get_next_message". [1]
Also, I can have one-to-many communication if I want with no code change on the part of the sender. Suppose I have a third ruby process which is also interested in "yummyfajitastasks". I can create a queue, and listen for messages with the key "yummyfajitastasks".
The ruby process might also be interested in "sorbitstasks" (which the c++ process is not), so it could then bind it's queue to the key "sorbitstasks. So now, "yummyfajitastasks" -> c++, ruby. "sorbitstasks" -> ruby.
Messages are persistent (if you want them to be) as well.
[1] The c bindings are a bit ugly, but you can wrap it fairly easily.