Member access operators
Accesses a member of its operand.
Operator name | Syntax | Overloadable | Prototype examples (for class T) | |
---|---|---|---|---|
Inside class definition | Outside class definition | |||
subscript | a[b] | Yes | R& T::operator[](S b); | N/A |
a[...] (since 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); |
|
Explanation
Built-in subscript operator provides access to an object pointed-to by the pointer or array operand.
Built-in indirection operator provides access to an object or function pointed-to by the pointer operand.
Built-in address-of operator creates a pointer pointing to the object or function operand.
Member of object and pointer to member of object operators provide access to a data member or member function of the object operand.
Built-in member of pointer and pointer to member of pointer operators provide access to a data member or member function of the class pointed-to by the pointer operand.
Built-in subscript operator
The subscript operator expressions have the form
expr1 [ expr2 ]
|
(1) | ||||||||
expr1 [{ expr , ... }]
|
(2) | (since C++11) | |||||||
expr1 [ expr2 , expr , ... ]
|
(3) | (since C++23) | |||||||
T
” or a prvalue of type “pointer to T
”, while the other expression (expr2 or expr1, respectively) must be a prvalue of unscoped enumeration or integral type. The result of this expression has the type T
. expr2 cannot be an unparenthesized comma expression.(since C++23)The built-in subscript expression E1[E2] is exactly identical to the expression *(E1 + E2) except for its value category (see below) and evaluation order(since C++17): the pointer operand (which may be a result of array-to-pointer conversion, and which must point to an element of some array or one past the end) is adjusted to point to another element of the same array, following the rules of pointer arithmetic, and is then dereferenced.
When applied to an array, the subscript expression is an lvalue if the array is an lvalue, and an xvalue if it isn't(since C++11).
When applied to a pointer, the subscript expression is always an lvalue.
The type T
is not allowed to be an incomplete type, even if the size or internal structure of T
is never used, as in &x[0].
Using an unparenthesized comma expression as second (right) argument of a subscript operator is deprecated. For example, a[b, c] is deprecated and a[(b, c)] is not. |
(since C++20) (until C++23) |
An unparenthesized comma expression cannot be second (right) argument of a subscript operator. For example, a[b, c] is either ill-formed or equivalent to a.operator[](b, c). Parentheses are needed to for using a comma expression as the subscript, e.g., a[(b, c)]. |
(since C++23) |
In overload resolution against user-defined operators, for every object type T
(possibly cv-qualified), the following function signature participates in overload resolution:
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"; // uses the [{...}] version }
Output:
4242
Built-in indirection operator
The indirection operator expressions have the form
* expr
|
|||||||||
The operand of the built-in indirection operator must be pointer to object or a pointer to function, and the result is the lvalue referring to the object or function to which expr points. If expr does not actually points to an object or function, the behavior is undefined (except for the case specified by typeid
).
A pointer to (possibly cv-qualified) void cannot be dereferenced. Pointers to other incomplete types can be dereferenced, but the resulting lvalue can only be used in contexts that allow an lvalue of incomplete type, e.g. when initializing a reference.
In overload resolution against user-defined operators, for every type T
that is either object type (possibly cv-qualified) or function type (not const- or ref-qualified), the following function signature participates in overload resolution:
T& operator*(T*); |
||
#include <iostream> int f() { return 42; } int main() { int n = 1; int* pn = &n; int& r = *pn; // lvalue can be bound to a reference int m = *pn; // indirection + lvalue-to-rvalue conversion int (*fp)() = &f; int (&fr)() = *fp; // function lvalue can be bound to a reference [](...){}(r, m, fr); // removes possible "unused variable" warnings }
Built-in address-of operator
The address-of operator expressions have the form
& expr
|
(1) | ||||||||
& class :: member
|
(2) | ||||||||
T
, operator&
creates and returns a prvalue of type T*
, with the same cv qualification, that is pointing to the object or function designated by the operand. If the operand has incomplete type, the pointer can be formed, but if that incomplete type happens to be a class that defines its own operator&, it is unspecified whether the built-in or the overload is used. For the operands of type with user-defined operator&, std::addressof may be used to obtain the true pointer.
Note that, unlike C99 and later C versions, there's no special case for the unary operator& applied to the result of the unary operator*.
If expr names an explicit object member function, expr must be a qualified identifier. Applying |
(since C++23) |
T
in class C
. Note that neither &member nor C::member nor even &(C::member) may be used to initialize a pointer to member.In overload resolution against user-defined operators, this operator does not introduce any additional function signatures: built-in address-of operator does not apply if there exists an overloaded operator& that is a viable function.
void f(int) {} void f(double) {} struct A { int i; }; struct B { void f(); }; int main() { int n = 1; int* pn = &n; // pointer int* pn2 = &*pn; // pn2 == pn int A::* mp = &A::i; // pointer to data member void (B::*mpf)() = &B::f; // pointer to member function void (*pf)(int) = &f; // overload resolution due to initialization context // auto pf2 = &f; // error: ambiguous overloaded function type auto pf2 = static_cast<void (*)(int)>(&f); // overload resolution due to cast }
Built-in member access operators
The member access operator expressions have the form
expr .template (optional) id-expr
|
(1) | ||||||||
expr ->template (optional) id-expr
|
(2) | ||||||||
expr . pseudo-destructor
|
(3) | ||||||||
expr -> pseudo-destructor
|
(4) | ||||||||
T*
.id-expr is a name of (formally, an identifier expression that names) a data member or member function of T
or of an unambiguous and accessible base class B
of T
(e.g. E1.E2 or E1->E2), optionally qualified (e.g. E1.B::E2 or E1->B::E2), optionally using template disambiguator (e.g. E1.template E2 or E1->template E2).
If a user-defined operator-> is called, operator-> is called again on the resulting value, recursively, until an operator-> is reached that returns a plain pointer. After that, built-in semantics are applied to that pointer.
The expression E1->E2 is exactly equivalent to (*E1).E2 for built-in types; that is why the following rules address only E1.E2.
In the expression E1.E2:
- If E2 is of reference type
T&
orT&&
(since C++11), the result is an lvalue of typeT
designating the object or function to which the reference is bound. - Otherwise, given the type of E2 as
T
, the result is an lvalue of typeT
designating that static data member.
- If E2 is of reference type
T&
orT&&
(since C++11), the result is an lvalue of typeT
designating the object or function to which the corresponding reference member of E1 is bound. - Otherwise, if E1 is an lvalue, the result is an lvalue designating that non-static data member of E1.
- Otherwise (if E1 is an rvalue(until C++17)xvalue (which may be materialized from prvalue)(since C++17)), the result is an rvalue(until C++11)xvalue(since C++11) designating that non-static data member of E1.
- If E2 is a static member function, the result is an lvalue designating that static member function. Essentially, E1 is evaluated and discarded in this case.
- Otherwise (E2 is a non-static member function), the result is a prvalue designating that non-static member function of E1.
T
, the result is an rvalue(until C++11)a prvalue(since C++11) of type T
whose value is the value of the enumerator.~
followed by the type name or decltype specifier designating the same type (minus cv-qualifications), optionally qualified, the result is a special kind of prvalue that can only be used as the left-hand operand of a function call operator, and for no other purposeoperator. cannot be overloaded, and for operator->, in overload resolution against user-defined operators, the built-in operator does not introduce any additional function signatures: built-in operator-> does not apply if there exists an overloaded operator-> that is a viable function.
#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()); }
Output:
1 2 11 4 1
If E2 is a non-static member and the result of E1 is an object whose type is not similar to the type of E1, the behavior is undefined:
struct A { int i; }; struct B { int j; }; struct D : A, B {}; void f() { D d; static_cast<B&>(d).j; // OK, object expression designates the B subobject of d reinterpret_cast<B&>(d).j; // undefined behavior }
Built-in pointer-to-member access operators
The member access operator expressions through pointers to members have the form
lhs .* rhs
|
(1) | ||||||||
lhs ->* rhs
|
(2) | ||||||||
T
.T*
.rhs must be an rvalue of type pointer to member (data or function) of T
or pointer to member of an unambiguous and accessible base class B
of T
.
The expression E1->*E2 is exactly equivalent to (*E1).*E2 for built-in types; that is why the following rules address only E1.*E2.
In the expression E1.*E2:
- if E1 is an lvalue, the result is an lvalue designating that data member,
- otherwise (if E1 is an rvalue(until C++17)xvalue (which may be materialized from prvalue)(since C++17)), the result is an rvalue(until C++11)xvalue(since C++11) designating that data member;
&
, the program is ill-formed unless the member function has the cv-qualifier const but not volatile(since C++20);
7) if E1 is an lvalue and E2 points to a member function with ref-qualifier
&& , the program is ill-formed. |
(since C++11) |
In overload resolution against user-defined operators, for every combination of types D
, B
, R
, where class type B
is either the same class as D
or an unambiguous and accessible base class of D
, and R
is either an object or function type, the following function signature participates in overload resolution:
R& operator->*(D*, R B::*); |
||
where both operands may be cv-qualified, in which case the return type's cv-qualification is the union of the cv-qualification of the operands.
#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'; }
Output:
7 14 15
Standard library
Subscript operator is overloaded by many standard container classes:
accesses specific bit (public member function of std::bitset<N> ) | |
provides indexed access to the managed array (public member function of std::unique_ptr<T,Deleter> ) | |
accesses the specified character (public member function of std::basic_string<CharT,Traits,Allocator> ) | |
access specified element (public member function of std::array<T,N> ) | |
access specified element (public member function of std::deque<T,Allocator> ) | |
access specified element (public member function of std::vector<T,Allocator> ) | |
access or insert specified element (public member function of std::map<Key,T,Compare,Allocator> ) | |
access or insert specified element (public member function of std::unordered_map<Key,T,Hash,KeyEqual,Allocator> ) | |
accesses an element by index (public member function of std::reverse_iterator<Iter> ) | |
(C++11) |
accesses an element by index (public member function of std::move_iterator<Iter> ) |
get/set valarray element, slice, or mask (public member function of std::valarray<T> ) | |
returns specified sub-match (public member function of std::match_results<BidirIt,Alloc> ) |
The indirection and member operators are overloaded by many iterators and smart pointer classes:
dereferences pointer to the managed object (public member function of std::unique_ptr<T,Deleter> ) | |
dereferences the stored pointer (public member function of std::shared_ptr<T> ) | |
accesses the managed object (public member function of std::auto_ptr<T> ) | |
dereferences the iterator (public member function of std::raw_storage_iterator<OutputIt,T> ) | |
dereferences the decremented underlying iterator (public member function of std::reverse_iterator<Iter> ) | |
no-op (public member function of std::back_insert_iterator<Container> ) | |
no-op (public member function of std::front_insert_iterator<Container> ) | |
no-op (public member function of std::insert_iterator<Container> ) | |
(C++11)(C++11)(deprecated in C++20) |
accesses the pointed-to element (public member function of std::move_iterator<Iter> ) |
returns the current element (public member function of std::istream_iterator<T,CharT,Traits,Distance> ) | |
no-op (public member function of std::ostream_iterator<T,CharT,Traits> ) | |
obtains a copy of the current character (public member function of std::istreambuf_iterator<CharT,Traits> ) | |
no-op (public member function of std::ostreambuf_iterator<CharT,Traits> ) | |
accesses the current match (public member function of std::regex_iterator<BidirIt,CharT,Traits> ) | |
accesses current submatch (public member function of std::regex_token_iterator<BidirIt,CharT,Traits> ) |
No standard library classes overload operator&. The best known example of overloaded operator& is the Microsoft COM class CComPtr
, although it can also appear in EDSLs such as boost.spirit.
No standard library classes overload operator->*. It was suggested that it could be part of smart pointer interface, and in fact is used in that capacity by actors in boost.phoenix, but is more common in EDSLs such as cpp.react.
Notes
Feature-test macro | Value | Std | Feature |
---|---|---|---|
__cpp_multidimensional_subscript |
202110L | (C++23) | Multidimensional subscript operator |
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 1213 | C++11 | subscripting an array rvalue resulted in lvalue | reclassified as xvalue |
CWG 1458 | C++98 | applying & to an lvalue of incomplete class type whichdeclares operator& resulted in undefined behavior |
it is unspecified which & is used |
CWG 1642 | C++98 | the rhs in built-in pointer-to-member access operators could be an lvalue | can only be an rvalue |
CWG 1800 | C++98 | when applying & to a non-static data member of amember anonymous union, it was unclear whether the anonymous union take a part in the result type |
the anonymous union is not included in the result type |
CWG 2614 | C++98 | the result of E1.E2 was unclear if E2 is a reference member or enumerator | made clear |
CWG 2725 | C++98 | if E2 is a static member function, E1.E2 is well-formed even if it is not the left hand opreand of operator() |
E1.E2 is ill-formed in this case |
CWG 2748 | C++98 | the behavior of E1->E2 was unclear if E1 is a null pointer and E2 refers to a static member |
the behavior is undefined in this case |
CWG 2813 | C++98 | E1 was not a discarded-value expression if E1.E2 names a static member or enumeration |
it is |
CWG 2823 | C++98 | the behavior of *expr was unclear if expr does not point to an object or function |
made clear |
See also
Common operators | ||||||
---|---|---|---|---|---|---|
assignment | increment decrement |
arithmetic | logical | comparison | member access |
other |
a = b |
++a |
+a |
!a |
a == b |
a[...] |
function call |
a(...) | ||||||
comma | ||||||
a, b | ||||||
conditional | ||||||
a ? b : c | ||||||
Special operators | ||||||
static_cast converts one type to another related type |
C documentation for Member access operators
|