Member access operators
피연산자의 멤버에 접근합니다.
| 연산자 이름 | 구문 | 오버로드 가능 | 프로토타입 예시 ( class T 의 경우) | |
|---|---|---|---|---|
| 클래스 정의 내부 | 클래스 정의 외부 | |||
| subscript | a [ b ] | Yes | R & T :: operator [ ] ( S b ) ; | N/A |
| a [ ... ] (C++23부터) | R & T :: operator [ ] ( ... ) ; | |||
| indirection | * a | Yes | R & T :: operator * ( ) ; | R & operator * ( T a ) ; |
| address-of | & a | Yes | R * T :: operator & ( ) ; | R * operator & ( T a ) ; |
| member of object | a. b | No | N/A | N/A |
| member of pointer | a - > b | Yes | R * T :: operator - > ( ) ; | N/A |
| pointer to member of object | a. * b | No | N/A | N/A |
| pointer to member of pointer | a - > * b | Yes | R & T :: operator - > * ( S b ) ; | R & operator - > * ( T a, S b ) ; |
|
||||
목차 |
설명
내장 첨자 연산자는 pointer 또는 array 피연산자가 가리키는 객체에 대한 접근을 제공합니다.
내장 간접 참조(indirection) 연산자는 포인터 피연산자가 가리키는 객체나 함수에 대한 접근을 제공합니다.
내장 address-of 연산자는 객체나 함수 피연산자를 가리키는 포인터를 생성합니다.
객체의 멤버 와 객체의 멤버에 대한 포인터 연산자는 객체 피연산자의 데이터 멤버나 멤버 함수에 대한 접근을 제공합니다.
내장 멤버 포인터 및 멤버 포인터에 대한 포인터 연산자는 포인터 피연산자가 가리키는 클래스의 데이터 멤버 또는 멤버 함수에 대한 접근을 제공합니다.
내장 첨자 연산자
첨자 연산자 표현식의 형식은 다음과 같습니다
expr1
[
expr2
]
|
(1) | ||||||||
expr1
[{
expr
, ...
}]
|
(2) | (C++11부터) | |||||||
expr1
[
expr2
,
expr
, ...
]
|
(3) | (C++23부터) | |||||||
T
타입을 가집니다.
expr2
는 괄호로 묶이지 않은
쉼표 표현식
일 수 없습니다.
(C++23부터)
내장 첨자 표현식 E1 [ E2 ] 는 값 범주(아래 참조) 및 평가 순서 (C++17부터) 를 제외하고 표현식 * ( E1 + E2 ) 와 완전히 동일합니다: 포인터 피연산자(배열-포인터 변환의 결과일 수 있으며, 어떤 배열의 요소나 끝의 다음을 가리켜야 함)는 포인터 연산 규칙에 따라 동일한 배열의 다른 요소를 가리키도록 조정된 후 역참조됩니다.
배열에 적용될 때, 첨자 표현식은 lvalue 입니다(배열이 lvalue인 경우). 배열이 lvalue가 아닌 경우 xvalue 입니다 (C++11부터) .
포인터에 적용될 때, 첨자 표현식은 항상 lvalue입니다.
타입
T
는
incomplete type
이어서는 안 되며, 이는
&
x
[
0
]
와 같이
T
의 크기나 내부 구조가 사용되지 않는 경우에도 마찬가지입니다.
|
괄호로 묶지 않은 쉼표 표현식 을 첨자 연산자의 두 번째(오른쪽) 인수로 사용하는 것은 deprecated되었습니다. 예를 들어, a [ b, c ] 은 deprecated되었으며, a [ ( b, c ) ] 은 그렇지 않습니다. |
(C++20부터)
(C++23 이전) |
|
괄호로 묶지 않은 쉼표 표현식 은 첨자 연산자의 두 번째(오른쪽) 인수로 사용할 수 없습니다. 예를 들어, a [ b, c ] 은 잘못된 형식이거나 a. operator [ ] ( b, c ) 과 동일합니다. 쉼표 표현식을 첨자로 사용하려면 괄호가 필요합니다. 예를 들어, a [ ( b, c ) ] 과 같습니다. |
(C++23부터) |
사용자 정의 연산자에 대한 오버로드 해결
에서, 모든 객체 타입
T
(cv-qualified 가능)에 대해 다음 함수 시그니처가 오버로드 해결에 참여합니다:
|
T
&
operator
[
]
(
T
*
,
std::
ptrdiff_t
)
;
|
||
|
T
&
operator
[
]
(
std::
ptrdiff_t
, T
*
)
;
|
||
#include <iostream> #include <map> #include <string> int main() { int a[4] = {1, 2, 3, 4}; int* p = &a[2]; std::cout << p[1] << p[-1] << 1[p] << (-1)[p] << '\n'; std::map<std::pair<int, int>, std::string> m; m[{1, 2}] = "abc"; // [{...}] 버전 사용 }
출력:
4242
내장 역참조 연산자
간접 연산자 표현식의 형식은 다음과 같습니다
*
expr
|
|||||||||
내장 역참조 연산자의 피연산자는 객체에 대한 포인터 또는 함수에 대한 포인터여야 하며, 결과는
expr
가 가리키는 객체나 함수를 참조하는 lvalue입니다. 만약
expr
이 실제로 객체나 함수를 가리키지 않는 경우, 동작은 정의되지 않습니다(
typeid
에 의해 지정된 경우 제외).
( cv 한정자가 있을 수 있는) void 에 대한 포인터는 역참조할 수 없습니다. 다른 불완전한 타입에 대한 포인터는 역참조할 수 있지만, 결과로 나오는 lvalue는 불완전한 타입의 lvalue를 허용하는 상황에서만 사용할 수 있습니다. 예를 들어 참조를 초기화할 때와 같습니다.
사용자 정의 연산자에 대한 오버로드 해결
에서, 객체 타입(possibly cv-qualified)이거나 함수 타입(not const- or ref-qualified)인 모든 타입
T
에 대해 다음 함수 시그니처가 오버로드 해결에 참여합니다:
|
T
&
operator
*
(
T
*
)
;
|
||
#include <iostream> int f() { return 42; } int main() { int n = 1; int* pn = &n; int& r = *pn; // lvalue는 참조에 바인딩될 수 있음 int m = *pn; // 간접 참조 + lvalue-to-rvalue 변환 int (*fp)() = &f; int (&fr)() = *fp; // 함수 lvalue는 참조에 바인딩될 수 있음 [](...){}(r, m, fr); // 가능한 "사용되지 않은 변수" 경고를 제거함 }
내장 주소 연산자
주소 연산자 표현식의 형식은 다음과 같습니다
&
expr
|
(1) | ||||||||
&
class
::
member
|
(2) | ||||||||
T
의 lvalue 표현식인 경우,
operator&
는 동일한 cv 한정자를 가지며 피연산자가 지정하는 객체나 함수를 가리키는
T*
타입의 prvalue를 생성하고 반환합니다. 피연산자가 불완전한 타입을 가지는 경우, 포인터를 형성할 수 있지만, 해당 불완전한 타입이 자체
operator
&
를 정의하는 클래스인 경우, 내장 연산자와 오버로드된 연산자 중 어느 것이 사용되는지 명시되지 않습니다. 사용자 정의
operator
&
를 가진 타입의 피연산자에 대해서는,
std::addressof
를 사용하여 실제 포인터를 얻을 수 있습니다.
C99 및 이후 C 버전과 달리, 단항
operator
&
가 단항
operator
*
의 결과에 적용되는 특별한 경우가 없음에 유의하십시오.
|
expr
이
명시적 객체 멤버 함수
를 지칭하는 경우,
expr
은 반드시
한정된 식별자
여야 합니다. 명시적 객체 멤버 함수를 지칭하는 비한정 식별자에
|
(C++23부터) |
C
클래스 내
T
타입의
pointer to member function
또는
pointer to data member
를 나타내는 prvalue입니다.
&
member
나
C
::
member
, 심지어
&
(
C
::
member
)
조차도 pointer to member를 초기화하는 데 사용될 수 없습니다.
사용자 정의 연산자에 대한 오버로드 해결 에서 이 연산자는 추가적인 함수 시그니처를 도입하지 않습니다: 내장 주소 연산자는 operator & 의 오버로드가 유효한 함수 로 존재하는 경우 적용되지 않습니다.
void f(int) {} void f(double) {} struct A { int i; }; struct B { void f(); }; int main() { int n = 1; int* pn = &n; // 포인터 int* pn2 = &*pn; // pn2 == pn int A::* mp = &A::i; // 데이터 멤버에 대한 포인터 void (B::*mpf)() = &B::f; // 멤버 함수에 대한 포인터 void (*pf)(int) = &f; // 초기화 컨텍스트로 인한 오버로드 해결 // auto pf2 = &f; // 오류: 모호한 오버로드된 함수 타입 auto pf2 = static_cast<void (*)(int)>(&f); // 캐스트로 인한 오버로드 해결 }
내장 멤버 접근 연산자
멤버 접근 연산자 표현식의 형식은 다음과 같습니다
expr
.template
(선택적)
id-expr
|
(1) | ||||||||
expr
->template
(선택적)
id-expr
|
(2) | ||||||||
expr
.
pseudo-destructor
|
(3) | ||||||||
expr
->
pseudo-destructor
|
(4) | ||||||||
T*
의 포인터 표현식이어야 합니다.
id-expr
는
T
의 데이터 멤버나 멤버 함수, 또는
T
의 명확하고 접근 가능한 베이스 클래스
B
의 데이터 멤버나 멤버 함수의 이름입니다(공식적으로는
식별자 표현식
으로 명명됨)(예:
E1.
E2
또는
E1
-
>
E2
), 선택적으로
한정됨
(예:
E1.
B
::
E2
또는
E1
-
>
B
::
E2
), 선택적으로
template
디스엠비규에이터
를 사용함(예:
E1.
template
E2
또는
E1
-
>
template
E2
).
사용자 정의 operator - > 가 호출되면, operator - > 는 결과 값에 대해 재귀적으로 다시 호출되며, 일반 포인터를 반환하는 operator - > 에 도달할 때까지 계속됩니다. 그 후에는 해당 포인터에 대해 내장된 의미론이 적용됩니다.
표현식 E1 - > E2 는 내장 타입에 대해 ( * E1 ) . E2 와 정확히 동일합니다. 따라서 다음 규칙들은 E1. E2 에 대해서만 다룹니다.
표현식 E1. E2 에서:
-
만약
E2
가 참조 타입
T&또는T&&(C++11부터) 인 경우, 결과는 해당 참조가 바인딩된 객체나 함수를 지정하는 타입T의 lvalue입니다. -
그렇지 않고,
E2
의 타입이
T로 주어진 경우, 결과는 해당 static data member를 지정하는 타입T의 lvalue입니다.
-
만약
E2
가 참조 타입
T&또는T&&(C++11부터) 인 경우, 결과는 해당 참조 멤버가 바인딩된 객체나 함수를 지정하는T타입의 lvalue입니다. - 그렇지 않고 E1 이 lvalue인 경우, 결과는 E1 의 해당 비정적 데이터 멤버를 지정하는 lvalue입니다.
- 그 외의 경우 ( E1 이 rvalue (C++17 이전) xvalue (prvalue에서 구체화 될 수 있음) (C++17부터) 인 경우), 결과는 rvalue (C++11 이전) xvalue (C++11부터) 로서 E1 의 해당 비정적 데이터 멤버를 지정합니다.
- 만약 E2 가 static member function 인 경우, 결과는 해당 static member function을 지정하는 lvalue입니다. 본질적으로 이 경우 E1 은 평가된 후 버려집니다.
- 그렇지 않은 경우 ( E2 가 non-static member function 인 경우), 결과는 E1 의 해당 non-static member function을 지정하는 prvalue입니다.
T
일 때, 결과는 열거자의 값을 가지는 타입
T
의
rvalue
(C++11 이전)
prvalue
(C++11 이후)
입니다.
~
다음에 동일한 타입(cv-qualifications 제외)을 지정하는
타입 이름
또는
decltype 지정자
가 오는 경우, 선택적으로
한정된
, 결과는 함수 호출 연산자의 왼쪽 피연산자로만 사용될 수 있고 다른 목적으로는 사용할 수 없는 특별한 종류의 prvalue입니다
operator. 는 오버로드할 수 없으며, operator - > 의 경우, 사용자 정의 연산자에 대한 오버로드 해결 에서 기본 제공 연산자는 추가 함수 시그니처를 도입하지 않습니다: operator - > 가 오버로드되어 있고 그것이 실행 가능 함수 인 경우 기본 제공 operator - > 는 적용되지 않습니다.
#include <cassert> #include <iostream> #include <memory> struct P { template<typename T> static T* ptr() { return new T; } }; template<typename T> struct A { A(int n): n(n) {} int n; static int sn; int f() { return 10 + n; } static int sf() { return 4; } class B {}; enum E {RED = 1, BLUE = 2}; void g() { typedef int U; // keyword template needed for a dependent template member int* p = T().template ptr<U>(); p->~U(); // U is int, calls int's pseudo destructor delete p; } }; template<> int A<P>::sn = 2; struct UPtrWrapper { std::unique_ptr<std::string> uPtr; std::unique_ptr<std::string>& operator->() { return uPtr; } }; int main() { A<P> a(1); std::cout << a.n << ' ' << a.sn << ' ' // A::sn also works << a.f() << ' ' << a.sf() << ' ' // A::sf() also works // << &a.f << ' ' // error: ill-formed if a.f is not the // left-hand operand of operator() // << a.B << ' ' // error: nested type not allowed << a.RED << ' '; // enumerator UPtrWrapper uPtrWrap{std::make_unique<std::string>("wrapped")}; assert(uPtrWrap->data() == uPtrWrap.operator->().operator->()->data()); }
출력:
1 2 11 4 1
만약 E2 가 비정적 멤버이고 E1 의 결과가 해당 타입과 유사(similar) 하지 않은 타입의 객체인 경우, 이 동작은 정의되지 않습니다:
struct A { int i; }; struct B { int j; }; struct D : A, B {}; void f() { D d; static_cast<B&>(d).j; // 정상, 객체 표현식이 d의 B 서브객체를 지정함 reinterpret_cast<B&>(d).j; // 정의되지 않은 동작 }
내장 포인터-대-멤버 접근 연산자
멤버 접근 연산자 표현식은 멤버에 대한 포인터를 통한 다음과 같은 형식을 가집니다.
lhs
.*
rhs
|
(1) | ||||||||
lhs
->*
rhs
|
(2) | ||||||||
태그 내부의 C++ 연산자(.*, ->*)는 번역하지 않고 원본을 유지했습니다. lhs와 rhs는 C++에서 일반적으로 사용되는 약어로, left-hand side(왼쪽 피연산자)와 right-hand side(오른쪽 피연산자)를 의미하지만, 전문 용어이므로 번역하지 않고 그대로 두었습니다.
T
의 표현식이어야 합니다.
T*
의 표현식이어야 합니다.
rhs
는
T
의 멤버 포인터(
데이터
또는
함수
) 타입의 rvalue이거나,
T
의 명확하고 접근 가능한 기본 클래스
B
의 멤버 포인터여야 합니다.
표현식 E1 - > * E2 는 내장 타입에 대해 ( * E1 ) . * E2 와 정확히 동일합니다. 따라서 다음 규칙들은 E1. * E2 만을 다룹니다.
표현식 E1. * E2 에서:
- 만약 E1 이 lvalue이면, 결과는 해당 데이터 멤버를 지정하는 lvalue입니다,
- 그렇지 않은 경우 (만약 E1 이 rvalue (C++17까지) xvalue (prvalue에서 구체화 될 수 있음) (C++17부터) 인 경우), 결과는 해당 데이터 멤버를 지정하는 rvalue (C++11까지) xvalue (C++11부터) 입니다;
&
를 가진 멤버 함수를 가리키는 경우, 프로그램은 ill-formed입니다
해당 멤버 함수가 cv-qualifier
const
를 가지지만
volatile
는 가지지 않는 경우는 제외
(C++20부터)
;
|
7)
만약
E1
이 lvalue이고
E2
가 ref-qualifier
&&
를 가진 멤버 함수를 가리키는 경우, 프로그램은 ill-formed입니다.
|
(C++11부터) |
사용자 정의 연산자에 대한 오버로드 해결
에서, 클래스 타입
B
가
D
와 동일한 클래스이거나
D
의 명확하고 접근 가능한 기본 클래스이며,
R
이 객체 또는 함수 타입인 모든 타입 조합
D
,
B
,
R
에 대해 다음 함수 시그니처가 오버로드 해결에 참여합니다:
|
R
&
operator
-
>
*
(
D
*
, R B
::
*
)
;
|
||
두 피연산자가 모두 cv 한정자(cv-qualified)일 수 있으며, 이 경우 반환 타입의 cv 한정자는 피연산자들의 cv 한정자의 합집합입니다.
#include <iostream> struct S { S(int n) : mi(n) {} mutable int mi; int f(int n) { return mi + n; } }; struct D : public S { D(int n) : S(n) {} }; int main() { int S::* pmi = &S::mi; int (S::* pf)(int) = &S::f; const S s(7); // s.*pmi = 10; // error: cannot modify through mutable std::cout << s.*pmi << '\n'; D d(7); // base pointers work with derived object D* pd = &d; std::cout << (d.*pf)(7) << ' ' << (pd->*pf)(8) << '\n'; }
출력:
7 14 15
표준 라이브러리
첨자 연산자는 많은 표준 컨테이너 클래스에서 오버로드됩니다:
|
특정 비트에 접근
(
std::bitset<N>
의
public member function)
|
|
|
관리되는 배열에 인덱스 기반 접근 제공
(
std::unique_ptr<T,Deleter>
의
public member function)
|
|
|
지정된 문자에 접근
(
std::basic_string<CharT,Traits,Allocator>
의
public member function)
|
|
|
지정된 요소에 접근
(
std::array<T,N>
의
public member function)
|
|
|
지정된 요소에 접근
(
std::deque<T,Allocator>
의
public member function)
|
|
|
지정된 요소에 접근
(
std::vector<T,Allocator>
의
public member function)
|
|
|
지정된 요소에 접근 또는 삽입
(
std::map<Key,T,Compare,Allocator>
의
public member function)
|
|
|
지정된 요소에 접근 또는 삽입
(
std::unordered_map<Key,T,Hash,KeyEqual,Allocator>
의
public member function)
|
|
|
인덱스로 요소에 접근
(
std::reverse_iterator<Iter>
의
public member function)
|
|
|
인덱스로 요소에 접근
(
std::move_iterator<Iter>
의
public member function)
|
|
|
valarray 요소, 슬라이스 또는 마스크 가져오기/설정
(
std::valarray<T>
의
public member function)
|
|
|
지정된 부분 일치 반환
(
std::match_results<BidirIt,Alloc>
의
public member function)
|
간접 참조 및 멤버 연산자는 많은 반복자와 스마트 포인터 클래스에 의해 오버로드됩니다:
|
관리 객체에 대한 포인터를 역참조함
(
std::unique_ptr<T,Deleter>
의
public member function)
|
|
|
저장된 포인터를 역참조함
(
std::shared_ptr<T>
의
public member function)
|
|
|
관리 객체에 접근함
(
std::auto_ptr<T>
의
public member function)
|
|
|
반복자를 역참조함
(
std::raw_storage_iterator<OutputIt,T>
의
public member function)
|
|
|
감소된 기본 반복자를 역참조함
(
std::reverse_iterator<Iter>
의
public member function)
|
|
|
no-op
(
std::back_insert_iterator<Container>
의
public member function)
|
|
|
no-op
(
std::front_insert_iterator<Container>
의
public member function)
|
|
|
no-op
(
std::insert_iterator<Container>
의
public member function)
|
|
|
가리키는 요소에 접근함
(
std::move_iterator<Iter>
의
public member function)
|
|
|
현재 요소를 반환함
(
std::istream_iterator<T,CharT,Traits,Distance>
의
public member function)
|
|
|
no-op
(
std::ostream_iterator<T,CharT,Traits>
의
public member function)
|
|
|
현재 문자 사본을 획득함
(
std::istreambuf_iterator<CharT,Traits>
의
public member function)
|
|
|
no-op
(
std::ostreambuf_iterator<CharT,Traits>
의
public member function)
|
|
|
현재 매치에 접근함
(
std::regex_iterator<BidirIt,CharT,Traits>
의
public member function)
|
|
|
현재 부분 매치에 접근함
(
std::regex_token_iterator<BidirIt,CharT,Traits>
의
public member function)
|
표준 라이브러리 클래스들은
operator
&
를 오버로드하지 않습니다. 오버로드된
operator
&
의 가장 잘 알려진 예는 Microsoft COM 클래스
CComPtr
이며, 비록 이것이
boost.spirit
와 같은 EDSL에서도 나타날 수 있습니다.
표준 라이브러리 클래스들은 operator - > * 를 오버로드하지 않습니다. 이것이 스마트 포인터 인터페이스 의 일부가 될 수 있다는 제안이 있었으며, 실제로 boost.phoenix 의 액터들이 이러한 용도로 사용하지만, cpp.react 와 같은 EDSL에서 더 일반적으로 사용됩니다.
참고 사항
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_multidimensional_subscript
|
202110L
|
(C++23) | 다차원 첨자 연산자 |
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 1213 | C++11 | 배열 rvalue에 대한 첨자 연산 결과가 lvalue였음 | xvalue로 재분류됨 |
| CWG 1458 | C++98 |
&
연산자를
operator & 를 선언하는 불완전 클래스 타입의 lvalue에 적용 시 미정의 동작이 발생했음 |
어떤
&
가 사용되는지
명시되지 않음 |
| CWG 1642 | C++98 | 내장 포인터-대-멤버 접근 연산자에서 rhs 가 lvalue일 수 있었음 | rvalue만 가능함 |
| CWG 1800 | C++98 |
&
연산자를 멤버 익명 유니온의
비정적 데이터 멤버에 적용할 때 익명 유니온이 결과 타입에 포함되는지 불명확했음 |
익명 유니온은
결과 타입에 포함되지 않음 |
| CWG 2614 | C++98 |
E2
가 참조 멤버나 열거자인 경우
E1. E2 의 결과가 불명확했음 |
명확해짐 |
| CWG 2725 | C++98 |
E2
가 정적 멤버 함수인 경우,
E1. E2 가 operator ( ) 의 왼쪽 피연산자가 아니더라도 올바른 형식이었음 |
이 경우
E1.
E2
는
올바르지 않은 형식임 |
| CWG 2748 | C++98 |
E1
가 널 포인터이고
E2
가 정적 멤버를
참조할 때 E1 - > E2 의 동작이 불명확했음 |
이 경우 동작은
미정의됨 |
| CWG 2813 | C++98 |
E1.
E2
가 정적 멤버나 열거형을
지칭할 때 E1 가 폐기 값 표현식이 아니었음 |
폐기 값 표현식임 |
| CWG 2823 | C++98 |
expr
이 객체나 함수를
가리키지 않을 때 * expr 의 동작이 불명확했음 |
명확해짐 |
참고 항목
| 일반 연산자 | ||||||
|---|---|---|---|---|---|---|
| 대입 |
증가
감소 |
산술 | 논리 | 비교 |
멤버
접근 |
기타 |
|
a
=
b
|
++
a
|
+
a
|
!
a
|
a
==
b
|
a
[
...
]
|
함수 호출
a ( ... ) |
|
쉼표
a, b |
||||||
|
조건부
a ? b : c |
||||||
| 특수 연산자 | ||||||
|
static_cast
관련된 타입 간 변환 수행
|
||||||
|
C 문서
for
멤버 접근 연산자
|