友元声明
友元声明出现在 类体 中,并授予函数或另一个类访问声明友元声明的类的私有和受保护成员的权限。
内容 |
[编辑] 语法
friend function-declaration |
(1) | ||||||||
friend function-definition |
(2) | ||||||||
friend elaborated-type-specifier ; |
(3) | (直到 C++26) | |||||||
friend simple-type-specifier ;
|
(4) | (自 C++11 起) (直到 C++26) | |||||||
friend friend-type-specifier-list ; |
(5) | (自 C++26 起) | |||||||
function-declaration | - | 一个 函数声明 |
function-definition | - | 一个 函数定义 |
elaborated-type-specifier | - | 一个 详细类型说明符 |
simple-type-specifier | - | 一个 简单类型说明符 |
typename-specifier | - | 关键字 typename 后跟一个限定标识符或限定的 简单模板标识符 |
friend-type-specifier-list | - | 一个非空的逗号分隔的 simple-type-specifier、elaborated-type-specifier 和 typename-specifier s 列表,每个说明符后面都可以跟省略号 (... ) |
[编辑] 描述
class Y { int data; // private member // the non-member function operator<< will have access to Y's private members friend std::ostream& operator<<(std::ostream& out, const Y& o); friend char* X::foo(int); // members of other classes can be friends too friend X::X(char), X::~X(); // constructors and destructors can be friends }; // friend declaration does not declare a member function // this operator<< still needs to be defined, as a non-member std::ostream& operator<<(std::ostream& out, const Y& y) { return out << y.data; // can access private member Y::data }
class X { int a; friend void friend_set(X& p, int i) { p.a = i; // this is a non-member function } public: void member_set(int i) { a = i; // this is a member function } };
class Y {}; class A { int data; // private data member class B {}; // private nested type enum { a = 100 }; // private enumerator friend class X; // friend class forward declaration (elaborated class specifier) friend Y; // friend class declaration (simple type specifier) (since C++11) // the two friend declarations above can be merged since C++26: // friend class X, Y; }; class X : A::B // OK: A::B accessible to friend { A::B mx; // OK: A::B accessible to member of friend class Y { A::B my; // OK: A::B accessible to nested member of friend }; int v[A::a]; // OK: A::a accessible to member of friend };
[edit] 模板友元
函数模板 和 类模板 声明都可以在任何非局部类或类模板中使用 friend
说明符(尽管只有函数模板可以在授予友元的类或类模板中定义)。在这种情况下,模板的每个特化都成为友元,无论它是隐式实例化、部分特化还是显式特化。
class A { template<typename T> friend class B; // every B<T> is a friend of A template<typename T> friend void f(T) {} // every f<T> is a friend of A };
友元声明不能引用部分特化,但可以引用完全特化
template<class T> class A {}; // primary template<class T> class A<T*> {}; // partial template<> class A<int> {}; // full class X { template<class T> friend class A<T*>; // Error friend class A<int>; // OK };
当友元声明引用函数模板的完全特化时,关键字 inline, constexpr(自 C++11 起), consteval(自 C++20 起) 和默认参数不能使用
template<class T> void f(int); template<> void f<int>(int); class X { friend void f<int>(int x = 1); // error: default args not allowed };
模板友元声明可以命名类模板 A 的成员,该成员可以是成员函数或成员类型(类型必须使用 详细类型说明符)。此类声明只有在嵌套名称说明符中的最后一个组件(最后一个 ::
左边的名称)是命名类模板的简单模板标识符(模板名称后跟尖括号中的参数列表)时才有效。此类模板友元声明的模板参数必须从简单模板标识符中推断出来。
在这种情况下,A 或 A 的部分特化的任何特化的成员都成为友元。这不会涉及实例化主模板 A 或 A 的部分特化:唯一的要求是 A 的模板参数从该特化中推断成功,并且将推断出的模板参数代入友元声明中会产生一个声明,该声明将是特化的成员的有效重新声明
// primary template template<class T> struct A { struct B {}; void f(); struct D { void g(); }; T h(); template<T U> T i(); }; // full specialization template<> struct A<int> { struct B {}; int f(); struct D { void g(); }; template<int U> int i(); }; // another full specialization template<> struct A<float*> { int *h(); }; // the non-template class granting friendship to members of class template A class X { template<class T> friend struct A<T>::B; // all A<T>::B are friends, including A<int>::B template<class T> friend void A<T>::f(); // A<int>::f() is not a friend because its signature // does not match, but e.g. A<char>::f() is a friend // template<class T> // friend void A<T>::D::g(); // ill-formed, the last part of the nested-name-specifier, // // D in A<T>::D::, is not simple-template-id template<class T> friend int* A<T*>::h(); // all A<T*>::h are friends: // A<float*>::h(), A<int*>::h(), etc template<class T> template<T U> // all instantiations of A<T>::i() and A<int>::i() are friends, friend T A<T>::i(); // and thereby all specializations of those function templates };
默认模板参数 仅允许在模板友元声明上使用,如果该声明是定义,并且此翻译单元中没有出现此函数模板的其他声明。 |
(自 C++11 起) |
[edit] 模板友元运算符
模板友元的常见用例是声明作用于类模板的非成员运算符重载,例如 operator<<(std::ostream&, const Foo<T>&) 对于某些用户定义的 Foo<T>.
此类运算符可以在类体中定义,其效果是为每个 T
生成一个单独的非模板 operator<< 并使该非模板 operator<< 成为其 Foo<T> 的友元
#include <iostream> template<typename T> class Foo { public: Foo(const T& val) : data(val) {} private: T data; // generates a non-template operator<< for this T friend std::ostream& operator<<(std::ostream& os, const Foo& obj) { return os << obj.data; } }; int main() { Foo<double> obj(1.23); std::cout << obj << '\n'; }
输出
1.23
或者函数模板必须在类体之前声明为模板,在这种情况下,Foo<T> 中的友元声明可以引用 operator<< 针对其 T
的完全特化
#include <iostream> template<typename T> class Foo; // forward declare to make function declaration possible template<typename T> // declaration std::ostream& operator<<(std::ostream&, const Foo<T>&); template<typename T> class Foo { public: Foo(const T& val) : data(val) {} private: T data; // refers to a full specialization for this particular T friend std::ostream& operator<< <> (std::ostream&, const Foo&); // note: this relies on template argument deduction in declarations // can also specify the template argument with operator<< <T>" }; // definition template<typename T> std::ostream& operator<<(std::ostream& os, const Foo<T>& obj) { return os << obj.data; } int main() { Foo<double> obj(1.23); std::cout << obj << '\n'; }
[edit] 链接
友元声明中不允许使用 存储类说明符。
如果函数或函数模板首先在友元声明中声明和定义,并且封闭类在 导出声明 中定义,则其名称具有与封闭类名称相同的链接。 |
(自 C++20 起) |
如果(直到 C++20)否则,如果(自 C++20 起) 函数或函数模板在友元声明中声明,并且可以访问 相应的非友元声明,则该名称具有从该先前声明确定的链接。
否则,由友元声明引入的名称的链接将按通常方式确定。
[edit] 说明
友谊不是传递的(你朋友的朋友不是你的朋友)。
友谊不是继承的(你朋友的孩子不是你的朋友,你的朋友也不是你孩子的朋友)。
访问说明符 对友元声明的含义没有影响(它们可以出现在 private: 或 public: 部分,没有区别)。
友元类声明不能定义新类(friend class X {}; 是错误的)。
当局部类将非限定函数或类声明为友元时,只有最内层非类范围内的函数和类会被 查找,而不是全局函数
class F {}; int f(); int main() { extern int g(); class Local // Local class in the main() function { friend int f(); // Error, no such function declared in main() friend int g(); // OK, there is a declaration for g in main() friend class F; // friends a local F (defined later) friend class ::F; // friends the global F }; class F {}; // local F }
在类或类模板 X
中的友元声明中首次声明的名称成为 X
的最内层封闭命名空间的成员,但对于查找不可见(除了考虑 X
的参数相关查找)除非提供命名空间范围内的匹配声明 - 有关详细信息,请参阅 命名空间。
功能测试宏 | 值 | 标准 | 特性 |
---|---|---|---|
__cpp_variadic_friend |
202403L | (C++26) | 可变参数友元声明 |
[edit] 关键字
[edit] 示例
流插入和提取运算符通常声明为非成员友元
#include <iostream> #include <sstream> class MyClass { int i; // friends have access to non-public, non-static static inline int id{6}; // and static (possibly inline) members friend std::ostream& operator<<(std::ostream& out, const MyClass&); friend std::istream& operator>>(std::istream& in, MyClass&); friend void change_id(int); public: MyClass(int i = 0) : i(i) {} }; std::ostream& operator<<(std::ostream& out, const MyClass& mc) { return out << "MyClass::id = " << MyClass::id << "; i = " << mc.i; } std::istream& operator>>(std::istream& in, MyClass& mc) { return in >> mc.i; } void change_id(int id) { MyClass::id = id; } int main() { MyClass mc(7); std::cout << mc << '\n'; // mc.i = 333*2; // error: i is a private member std::istringstream("100") >> mc; std::cout << mc << '\n'; // MyClass::id = 222*3; // error: id is a private member change_id(9); std::cout << mc << '\n'; }
输出
MyClass::id = 6; i = 7 MyClass::id = 6; i = 100 MyClass::id = 9; i = 100
[edit] 缺陷报告
以下行为改变的缺陷报告已追溯应用于以前发布的 C++ 标准。
DR | 应用于 | 已发布的行为 | 正确的行为 |
---|---|---|---|
CWG 45 | C++98 | 嵌套在友元中的类的成员T 的类对 T 没有特殊访问权限 |
嵌套类具有与 封闭类相同的访问权限 |
CWG 500 | C++98 | T 的友元类不能从 T 的私有或受保护的成员继承,但其嵌套类可以 |
两者都可以从 此类成员继承 |
CWG 1439 | C++98 | 针对非局部友元声明的规则 类没有涵盖模板声明 |
已涵盖 |
CWG 1477 | C++98 | 在类或类模板中,友元声明中首次声明的名称 如果在另一个命名空间范围内提供匹配的 声明,则对于查找不可见 |
在这种情况下,它对 查找可见 |
CWG 1804 | C++98 | 当类模板的成员成为友元时,相应 类模板的部分特化的特化的成员 模板不是授予友谊的类的友元 |
此类成员 也是友元 |
CWG 2379 | C++11 | 引用函数模板的完全特化的友元声明 可以声明为 constexpr |
禁止 |
CWG 2588 | C++98 | 由友元声明引入的名称的链接不清楚 | 已澄清 |
[edit] 参考文献
- C++23 标准 (ISO/IEC 14882:2024)
- 11.8.4 友元 [class.friend]
- 13.7.5 友元 [temp.friend]
- C++20 标准 (ISO/IEC 14882:2020)
- 11.9.3 友元 [class.friend]
- 13.7.4 友元 [temp.friend]
- C++17 标准 (ISO/IEC 14882:2017)
- 14.3 友元 [class.friend]
- 17.5.4 友元 [temp.friend]
- C++14 标准 (ISO/IEC 14882:2014)
- 11.3 友元 [class.friend]
- 14.5.4 友元 [temp.friend]
- C++11 标准 (ISO/IEC 14882:2011)
- 11.3 友元 [class.friend]
- 14.5.4 友元 [temp.friend]
- C++98 标准 (ISO/IEC 14882:1998)
- 11.3 友元 [class.friend]
- 14.5.3 友元 [temp.friend]
[edit] 另请参阅
类类型 | 定义包含多个数据成员的类型 |
访问说明符 | 定义类成员的可见性 |