Usage flexibility of Deck class
1: Yes, your Card class should expose its rank and suit through a public interface. (See the previous thread and @user225770's answer for value.) It should at least give read-only access, but write access may not be necessary. (It is probably better to create a copy than to alter a card.)

The simplest indication of this is your output stream operator overload. The fact that it needs access to these members is a good indication that other client code (i.e. code using this class) will need access as well.

2: It depends, but in either case it should be modified a bit. Exceptions are used for exceptional circumstances, scenarios that you don't expect to run into a lot. If it's rare that your deck runs out of cards, then throwing an exception is fine. However, you shouldn't throw an exception for your own function. Let the exception propagate to the calling code, and let them decide how to handle the exception.

However, if it's a common occurrence that your deck runs out of cards, you should use another approach. What immediately comes to mind is to simply reduce the chance of this happening by checking empty() before calling deal(). You can verify this behavior by adding an assert at the beginning of the function.

(What you are technically doing is defining a precondition for your function. In short, this means that your function requires and assumes that the deck is not empty when deal() is called.)

3: No, I don't think this solution is over-engineered. However, a more interesting question is "How can I tell if I have over-engineered my solution?". Why, thank you for asking! This leads me to my next, and perhaps most important point:

Start doing unit tests. You are writing good code. The best way for you to improve now is by doing test-driven development. By writing unit tests, several of your questions will answer itself. For example: Should my class expose this member through its interface? Unit tests will answer this question for you by letting you know what you need access to to test and use your code in unit tests.

Test-driven development will let you know if your code is over-engineered. You write a test that works as a requirement to your code. Do you need to do X to make the test pass? Then implement it. Don't you? Then You Ain't Gonna Need It (yet).