類內聲明的友元函數,在外層作用域不可見(不進入外層命名空間)。但可通過 ADL (Argument-Dependent Lookup) 根據實參類型在關聯類/命名空間中找到。
template <typename T>
struct C {
friend void f(); // 無參數友元
friend void f(C<T> const&); // 有參數友元
};
int main() {
C<int>* p = new C<int>();
f(); // ❌ 編譯錯誤:外層不可見,無 ADL 觸發
f(*p); // ✅ 編譯通過:ADL 在 C<int> 中找到 f(C<int> const&)
}
觸發條件:函數名為非限定名(無 ::、->、. 前綴)且有至少一個實參。
關聯集合構建規則:對每個實參類型,確定其關聯類與關聯命名空間:
ADL 會在所有關聯類與命名空間中查找同名函數,忽略 using 引入的名字。
f():無實參 → 不觸發 ADL → 只在普通查找範圍找 → 類內友元聲明不進入外層作用域 → 找不到f(*p):實參類型 C<int> → 關聯集合包含 C<int> → ADL 在 C<int> 中找到友元聲明 f(C<int> const&) → 成功friend 修飾這是 C++ 名字查找機制中最微妙也最實用的角落之一,掌握它能避免大量「明明聲明了卻找不到」的困惑。