Название, назначенное декларацией друга, должно быть доступно в область действия класса, содержащего декларацию друга.
Причина довольно проста; Члены private должны соблюдать ясное и определенное правило:
Член класса может быть
private; то есть его имя может использоваться только членами и друзьями класса, в котором он объявлен.
Разрешить имена частных членов в объявлениях внутри несвязанных классов будет нарушать это правило: он позволяет другому классу зависеть от детализации реализации, не будучи явно разрешенным. Это становится проблематичным, например, при изменении имени частного лица, типа или подписи или его полного удаления; который предназначен для того, чтобы не нарушить интерфейс этого класса.
Предполагается, что идея x::xxprivate заключается в том, что x::xx представляет собой деталь реализации, на которую не следует полагаться другими классами. Это не просто означает, что x::xx нельзя вызывать другими классами, это означает, или, скорее, это должно означать, что, например, переименование x::xx в x::xy не должно нарушать ничего, кроме самого класса и друзей класса.
В вашем случае переименование x::xx до x::xy приведет к ошибке класса y, хотя это не является другом x.
Чтобы избежать этого, сделайте y другом x, так что y может получить доступ к xprivate членам. Затем он может объявить x::xx как friend.
(Примечание: более прямой ответ на вопрос "Почему компилятор не разрешает это?" - это "Потому что стандарт не позволяет этого". Это, естественно, приводит к следующему вопросу "Почему стандарт не разрешите это?". Я пытаюсь ответить на этот последующий вопрос.)