Namespaces
Variants

Friend declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Access specifiers
friend specifier

Class-specific function properties
Special member functions
Templates
Miscellaneous

friend 선언은 클래스 본문 에 나타나며, friend 선언이 나타나는 클래스의 private 및 protected 멤버에 대한 접근 권한을 함수나 다른 클래스에 부여합니다.

목차

구문

friend 함수 선언 (1)
friend 함수 정의 (2)
friend 상세 타입 지정자 ; (3) (C++26 이전)
friend 단순 타입 지정자 ;

friend 타입이름 지정자 ;

(4) (C++11 이후)
(C++26 이전)
friend friend 타입 지정자 목록 ; (5) (C++26 이후)
1,2) 함수 friend 선언.
3-5) 클래스 프렌드 선언.
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 목록, 각 지정자 뒤에는 생략 부호( ... )가 올 수 있음

설명

1) 이 클래스의 프렌드로 함수 하나 또는 여러 개를 지정합니다:
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
}
2) (비- local 클래스 정의에서만 허용됨) 비멤버 함수를 정의하고 동시에 이 클래스의 friend로 만듭니다. 이러한 비멤버 함수는 항상 inline 으로 처리되며, named module 에 연결된 경우는 예외입니다 (C++20부터) .
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
    }
};
3,4) 이 클래스의 friend로 클래스를 지정합니다. 이는 friend의 멤버 선언과 정의가 이 클래스의 private 및 protected 멤버에 접근할 수 있으며, 또한 friend가 이 클래스의 private 및 protected 멤버로부터 상속받을 수 있음을 의미합니다.
3) 클래스는 elaborated-type-specifier 로 명명됩니다. 이 friend 선언에서 사용되는 클래스의 이름은 이전에 선언될 필요가 없습니다.
4) 클래스는 simple-type-specifier 또는 typename-specifier 로 명명됩니다. 명명된 타입이 클래스 타입이 아닌 경우, 이 friend 선언은 무시됩니다. 이 선언은 새로운 타입을 전방 선언하지 않습니다.
5) friend-type-specifier-list 내의 모든 클래스를 이 클래스의 friend로 지정합니다. 이는 friend의 멤버 선언과 정의가 이 클래스의 private 및 protected 멤버에 접근할 수 있음을 의미하며, 또한 friend가 이 클래스의 private 및 protected 멤버로부터 상속받을 수 있음을 의미합니다. 명명된 타입이 클래스 타입이 아닌 경우, 이 friend 선언에서 무시됩니다.
friend-type-specifier-list 의 각 지정자는 줄임표가 뒤따르지 않는 경우 클래스를 지정하며, 그렇지 않은 경우 pack expansion 이 적용됩니다.
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
};

템플릿 프렌드

function template class template 선언은 모두 friend 지정자를 사용하여 비-로컬 클래스나 클래스 템플릿 내에서 나타날 수 있습니다(단, 함수 템플릿만이 우정을 부여하는 클래스나 클래스 템플릿 내에서 정의될 수 있습니다). 이 경우, 템플릿의 모든 특수화는 암시적으로 인스턴스화되거나, 부분적으로 특수화되거나, 명시적으로 특수화되는지 여부에 관계없이 friend가 됩니다.

class A
{
    template<typename T>
    friend class B; // 모든 B<T>는 A의 friend입니다
    template<typename T>
    friend void f(T) {} // 모든 f<T>는 A의 friend입니다
};

friend 선언은 부분 특수화를 참조할 수 없지만, 완전 특수화는 참조할 수 있습니다:

template<class T>
class A {};      // 기본 템플릿
template<class T>
class A<T*> {};  // 부분 특수화
template<>
class A<int> {}; // 완전 특수화
class X
{
    template<class T>
    friend class A<T*>;  // 오류
    friend class A<int>; // 정상
};

friend 선언이 함수 템플릿의 완전 특수화를 참조할 때, inline , constexpr (since C++11) , consteval (since C++20) 키워드와 기본 인수는 사용할 수 없습니다:

template<class T>
void f(int);
template<>
void f<int>(int);
class X
{
    friend void f<int>(int x = 1); // 오류: 기본 인자는 허용되지 않음
};

템플릿 friend 선언은 클래스 템플릿 A의 멤버를 지정할 수 있으며, 이는 멤버 함수 또는 멤버 타입(해당 타입은 elaborated-type-specifier 를 사용해야 함)이 될 수 있습니다. 이러한 선언은 nested-name-specifier의 마지막 구성 요소(마지막 :: 의 왼쪽에 있는 이름)가 클래스 템플릿을 지정하는 simple-template-id(템플릿 이름 뒤에 꺾쇠 괄호 안의 인수 목록)인 경우에만 올바른 형식을 가집니다. 이러한 템플릿 friend 선언의 템플릿 매개변수는 simple-template-id로부터 추론 가능해야 합니다.

이 경우, A의 임의의 특수화 또는 A의 부분 특수화의 멤버가 friend가 됩니다. 이는 기본 템플릿 A나 A의 부분 특수화를 인스턴스화하는 것을 포함하지 않습니다: 유일한 요구사항은 해당 특수화로부터 A의 템플릿 매개변수 추론이 성공해야 하며, 추론된 템플릿 인수를 friend 선언에 대입했을 때 해당 특수화의 멤버 유효한 재선언을 생성할 수 있는 선언이어야 한다는 것입니다:

// 기본 템플릿
template<class T>
struct A
{ 
    struct B {};
    void f();
    struct D { void g(); };
    T h();
    template<T U>
    T i();
};
// 완전 특수화
template<>
struct A<int>
{
    struct B {};
    int f();
    struct D { void g(); };
    template<int U>
    int i();
};
// 다른 완전 특수화
template<>
struct A<float*>
{
    int *h();
};
// 클래스 템플릿 A의 멤버들에게 friend 권한을 부여하는 비템플릿 클래스
class X
{
    template<class T>
    friend struct A<T>::B; // 모든 A<T>::B는 friend입니다. A<int>::B를 포함합니다
    template<class T>
    friend void A<T>::f(); // A<int>::f()는 시그니처가 일치하지 않으므로 friend가 아니지만,
                           // 예를 들어 A<char>::f()는 friend입니다
//  template<class T>
//  friend void A<T>::D::g(); // 잘못된 형식입니다. 중첩 이름 지정자의 마지막 부분인
//                            // A<T>::D::의 D가 단순 템플릿 ID가 아닙니다
    template<class T>
    friend int* A<T*>::h(); // 모든 A<T*>::h는 friend입니다:
                            // A<float*>::h(), A<int*>::h() 등
    template<class T> 
    template<T U>       // 모든 A<T>::i()와 A<int>::i()의 인스턴스화는 friend이며,
    friend T A<T>::i(); // 따라서 해당 함수 템플릿의 모든 특수화도 friend입니다
};

기본 템플릿 인수 는 선언이 정의이고 이 번역 단위에 이 함수 템플릿의 다른 선언이 나타나지 않는 경우에만 템플릿 friend 선언에서 허용됩니다.

(C++11부터)

템플릿 프렌드 연산자

템플릿 friend의 일반적인 사용 사례는 클래스 템플릿에 작용하는 비멤버 연산자 오버로드의 선언입니다, 예를 들어 operator << ( std:: ostream & , const Foo < T > & ) 와 같은 연산자를 사용자 정의 Foo < T > 에 대해 선언하는 경우입니다.

이러한 연산자는 클래스 본문 내에서 정의될 수 있으며, 이는 각각의 T 에 대해 별도의 비템플릿 operator << 를 생성하는 효과가 있고, 해당 비템플릿 operator << Foo < T > 의 friend로 만듭니다:

#include <iostream>
template<typename T>
class Foo
{
public:
    Foo(const T& val) : data(val) {}
private:
    T data;
    // 이 T에 대한 비템플릿 operator<< 생성
    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 > 내의 friend 선언은 해당 T 에 대한 operator << 의 완전 특수화를 참조할 수 있습니다:

#include <iostream>
template<typename T>
class Foo; // 함수 선언을 위해 전방 선언
template<typename T> // 선언
std::ostream& operator<<(std::ostream&, const Foo<T>&);
template<typename T>
class Foo
{
public:
    Foo(const T& val) : data(val) {}
private:
    T data;
    // 이 특정 T에 대한 완전 특수화를 참조
    friend std::ostream& operator<< <> (std::ostream&, const Foo&);
    // 참고: 이는 선언에서의 템플릿 인수 추론에 의존함
    // "operator<< <T>"로 템플릿 인수를 지정할 수도 있음
};
// 정의
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';
}

링키지

스토리지 클래스 지정자 는 friend 선언에서 허용되지 않습니다.

함수 또는 함수 템플릿이 friend 선언에서 처음 선언되고 정의되며, 둘러싸는 클래스가 내보내기 선언 내에서 정의된 경우, 해당 이름은 둘러싸는 클래스의 이름과 동일한 링크를 가집니다.

(since C++20)

만약 (C++20 이전) 그렇지 않으면, 만약 (C++20 이후) 함수나 함수 템플릿이 friend 선언에서 선언되고, 해당하는 non-friend 선언 이 도달 가능한 경우, 해당 이름은 이전 선언에서 결정된 링크를 갖습니다.

그렇지 않으면, friend 선언에 의해 도입된 이름의 링크는 일반적인 방식으로 결정됩니다.

참고 사항

친구 관계는 이행적이지 않습니다 (당신 친구의 친구는 당신의 친구가 아닙니다).

친구 관계는 상속되지 않습니다 (친구의 자녀는 당신의 친구가 아니며, 당신의 친구는 당신 자녀의 친구가 아닙니다).

접근 지정자 는 friend 선언의 의미에 영향을 주지 않습니다 (이들은 private : 섹션이나 public : 섹션에 나타날 수 있으며, 차이가 없습니다).

friend 클래스 선언은 새로운 클래스를 정의할 수 없습니다 ( friend class X { } ; 는 오류입니다).

지역 클래스가 한정되지 않은 함수나 클래스를 friend로 선언할 때, 가장 안쪽의 비클래스 범위에 있는 함수와 클래스만 조회되며 , 전역 함수는 조회되지 않습니다:

class F {};
int f();
int main()
{
    extern int g();
    class Local // main() 함수 내의 지역 클래스
    {
        friend int f(); // 오류: main() 내에 선언된 해당 함수 없음
        friend int g(); // 정상: main() 내에 g 선언 존재
        friend class F; // 지역 F와 friend 관계 설정(나중에 정의됨)
        friend class ::F; // 전역 F와 friend 관계 설정
    };
    class F {}; // 지역 F
}

클래스나 클래스 템플릿 X 내부의 friend 선언에서 처음 선언된 이름은 X 의 가장 안쪽에 둘러싸인 네임스페이스의 멤버가 되지만, ( X 를 고려하는 인수 종속 lookup을 제외하고) 네임스페이스 범위에서 일치하는 선언이 제공되지 않는 한 lookup에 보이지 않습니다. 자세한 내용은 namespaces 를 참조하십시오.

기능 테스트 매크로 표준 기능
__cpp_variadic_friend 202403L (C++26) 가변 친구 선언

키워드

friend

예제

스트림 삽입 및 추출 연산자는 종종 비멤버 friend 함수로 선언됩니다:

#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

결함 보고서

다음 동작 변경 결함 보고서는 이전에 게시된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 45 C++98 T 의 friend 클래스에 중첩된 클래스의 멤버들이
T 에 대한 특별한 접근 권한을 갖지 않음
중첩 클래스는 외부 클래스와
동일한 접근 권한을 가짐
CWG 500 C++98 T 의 friend 클래스는 T 의 private 또는
protected 멤버로부터 상속받을 수 없지만,
그 중첩 클래스는 상속받을 수 있음
둘 다 해당 멤버로부터
상속받을 수 있음
CWG 1439 C++98 비지역 클래스에서 friend 선언을 대상으로 하는 규칙이
템플릿 선언을 포함하지 않음
포함됨
CWG 1477 C++98 클래스나 클래스 템플릿 내의 friend 선언에서 처음 선언된 이름은
일치하는 선언이 다른 네임스페이스 범위에서 제공되는 경우
조회에 보이지 않음
이 경우 조회에
보이게 됨
CWG 1804 C++98 클래스 템플릿의 멤버를 friend로 선언할 때,
해당 클래스 템플릿의 부분 특수화의 특수화에 해당하는
멤버는 friendship을 부여하는 클래스의 friend가 아님
해당 멤버들도
friend임
CWG 2379 C++11 함수 템플릿의 완전 특수화를 참조하는 friend 선언을
constexpr로 선언할 수 있었음
금지됨
CWG 2588 C++98 friend 선언으로 도입된 이름들의 링크age가 불명확했음 명확해짐

참조문헌

  • C++23 표준 (ISO/IEC 14882:2024):
  • 11.8.4 Friends [class.friend]
  • 13.7.5 Friends [temp.friend]
  • C++20 표준(ISO/IEC 14882:2020):
  • 11.9.3 Friends [class.friend]
  • 13.7.4 Friends [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 Friends [class.friend]
  • 14.5.3 Friends [temp.friend]

참고 항목

Class types 여러 데이터 멤버를 보유하는 타입을 정의함
Access specifiers 클래스 멤버의 가시성을 정의함