Member access operators
Обращается к члену своего операнда.
| Название оператора | Синтаксис | Перегружаемый ли | Примеры прототипов (для class T ) | |
|---|---|---|---|---|
| Внутри определения класса | Вне определения класса | |||
| индексация | a [ b ] | Да | R & T :: operator [ ] ( S b ) ; | Н/П |
| a [ ... ] (since C++23) | R & T :: operator [ ] ( ... ) ; | |||
| разыменование | * a | Да | R & T :: operator * ( ) ; | R & operator * ( T a ) ; |
| взятие адреса | & a | Да | R * T :: operator & ( ) ; | R * operator & ( T a ) ; |
| доступ к члену объекта | a. b | Нет | Н/П | Н/П |
| доступ к члену через указатель | a - > b | Да | R * T :: operator - > ( ) ; | Н/П |
| доступ к члену-указателю объекта | a. * b | Нет | Н/П | Н/П |
| доступ к члену-указателю через указатель | a - > * b | Да | R & T :: operator - > * ( S b ) ; | R & operator - > * ( T a, S b ) ; |
|
||||
Содержание |
Объяснение
Встроенный оператор индексации предоставляет доступ к объекту, на который указывает указатель или массив .
Встроенный оператор косвенного обращения предоставляет доступ к объекту или функции, на которые указывает операнд-указатель.
Встроенный address-of оператор создает указатель, указывающий на объект или функцию-операнд.
Оператор доступа к члену объекта и оператор доступа к члену объекта через указатель предоставляют доступ к элементу данных или функции-члену операнда объекта.
Встроенные операторы member of pointer и pointer to member of pointer предоставляют доступ к элементу данных или функции-члену класса, на который указывает операнд-указатель.
Встроенный оператор индексации
Выражения с оператором индексации имеют вид
expr1
[
expr2
]
|
(1) | ||||||||
expr1
[{
expr
, ...
}]
|
(2) | (начиная с C++11) | |||||||
expr1
[
expr2
,
expr
, ...
]
|
(3) | (начиная с C++23) | |||||||
T
» или prvalue типа «указатель на
T
», тогда как другое выражение (
expr2
или
expr1
, соответственно) должно быть prvalue неограниченного перечисления или целочисленного типа. Результат этого выражения имеет тип
T
.
expr2
не может быть непомещённым в скобки
выражением с запятой
.
(начиная с C++23)
Встроенное выражение индексации E1 [ E2 ] точно идентично выражению * ( E1 + E2 ) за исключением его категории значения (см. ниже) и порядка вычисления (начиная с C++17) : указатель-операнд (который может быть результатом преобразования массива в указатель и должен указывать на элемент массива или за его пределы) корректируется для указания на другой элемент того же массива, следуя правилам арифметики указателей , и затем разыменовывается.
При применении к массиву, выражение индексации является lvalue если массив является lvalue, и xvalue если это не так (начиная с C++11) .
При применении к указателю, выражение с индексом всегда является lvalue.
Тип
T
не может быть
неполным типом
, даже если размер или внутренняя структура
T
никогда не используется, как в
&
x
[
0
]
.
|
Использование не заключенного в скобки выражения с запятой в качестве второго (правого) аргумента оператора индексации устарело. Например, a [ b, c ] устарело, а a [ ( b, c ) ] - нет. |
(начиная с C++20)
(до C++23) |
|
Не заключенное в скобки выражение с запятой не может быть вторым (правым) аргументом оператора индексации. Например, a [ b, c ] является либо ошибочным, либо эквивалентным a. operator [ ] ( b, c ) . Для использования выражения с запятой в качестве индекса необходимы скобки, например, a [ ( b, c ) ] . |
(начиная с C++23) |
В
разрешении перегрузки для пользовательских операторов
, для каждого объектного типа
T
(возможно, с квалификаторами cv), следующая сигнатура функции участвует в разрешении перегрузки:
|
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
Встроенный оператор косвенной адресации
Выражения с оператором косвенного обращения имеют вид
*
выражение
|
|||||||||
Операнд встроенного оператора разыменования должен быть указателем на объект или указателем на функцию, и результатом является lvalue, ссылающееся на объект или функцию, на которую указывает
expr
. Если
expr
фактически не указывает на объект или функцию, поведение не определено (за исключением случая, указанного в
typeid
).
Указатель на (возможно cv -квалифицированный) void не может быть разыменован. Указатели на другие неполные типы могут быть разыменованы, но результирующее lvalue может использоваться только в контекстах, допускающих lvalue неполного типа, например при инициализации ссылки.
В
разрешении перегрузки для пользовательских операторов
, для каждого типа
T
, который является либо объектным типом (возможно, с cv-квалификаторами), либо функциональным типом (без const- или ref-квалификаторов), следующая сигнатура функции участвует в разрешении перегрузки:
|
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; // function lvalue может быть связано со ссылкой [](...){}(r, m, fr); // убирает возможные предупреждения "неиспользуемая переменная" }
Встроенный оператор взятия адреса
Выражения оператора взятия адреса имеют вид
&
expr
|
(1) | ||||||||
&
class
::
member
|
(2) | ||||||||
T
,
operator&
создаёт и возвращает prvalue типа
T*
с той же cv-квалификацией, который указывает на объект или функцию, обозначенную операндом. Если операнд имеет неполный тип, указатель может быть сформирован, но если этот неполный тип оказывается классом, который определяет собственный
operator
&
, не определено, будет использована встроенная или перегруженная операция. Для операндов типа с пользовательским
operator
&
может использоваться
std::addressof
для получения истинного указателя.
Отметим, что в отличие от C99 и более поздних версий C, нет специального случая для унарного
operator
&
, применённого к результату унарного
operator
*
.
|
Если
expr
именует
явную объектную функцию-член
,
expr
должен быть
квалифицированным идентификатором
. Применение
|
(since C++23) |
T
в классе
C
. Заметим, что ни
&
member
, ни
C
::
member
, ни даже
&
(
C
::
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
.
псевдо-деструктор
|
(3) | ||||||||
expr
->
псевдо-деструктор
|
(4) | ||||||||
T*
.
id-expr
— это имя (формально,
выражение-идентификатор
, которое именует) элемента данных или функции-члена
T
или однозначного и доступного базового класса
B
для
T
(например,
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&&(since C++11) , результатом является lvalue типаT, обозначающее объект или функцию, на которую ссылка указывает. -
В противном случае, при условии что тип
E2
равен
T, результатом является lvalue типаT, обозначающее этот static data member.
-
Если
E2
имеет ссылочный тип
T&илиT&&(начиная с C++11) , результатом является lvalue типаT, обозначающее объект или функцию, на которую привязан соответствующий ссылочный член E1 . - Иначе, если E1 является lvalue, результатом является lvalue, обозначающее этот нестатический член-данных E1 .
- Иначе (если E1 является rvalue (до C++17) xvalue (которое может быть материализовано из prvalue) (начиная с C++17) ), результатом является rvalue (до C++11) xvalue (начиная с C++11) , обозначающее этот нестатический член-данных E1 .
- Если E2 является статической функцией-членом , результатом является lvalue, обозначающее эту статическую функцию-член. По сути, E1 вычисляется и отбрасывается в этом случае.
- В противном случае ( E2 является нестатической функцией-членом ), результатом является prvalue, обозначающее эту нестатическую функцию-член E1 .
T
, результатом будет
rvalue
(до C++11)
prvalue
(начиная с C++11)
типа
T
, значение которого равно значению перечислителя.
~
за которым следует
имя типа
или
спецификатор decltype
обозначающий тот же тип (без cv-квалификаторов), опционально
квалифицированный
, результатом является специальный вид 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; // ключевое слово template необходимо для зависимого шаблонного члена int* p = T().template ptr<U>(); p->~U(); // U - это int, вызывает псевдодеструктор int 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 также работает << a.f() << ' ' << a.sf() << ' ' // A::sf() также работает // << &a.f << ' ' // ошибка: некорректно, если a.f не является // левым операндом operator() // << a.B << ' ' // ошибка: вложенный тип не разрешен << a.RED << ' '; // перечислитель UPtrWrapper uPtrWrap{std::make_unique<std::string>("wrapped")}; assert(uPtrWrap->data() == uPtrWrap.operator->().operator->()->data()); }
Вывод:
1 2 11 4 1
Если E2 является нестатическим членом и результат E1 представляет собой объект, тип которого не подобен типу E1 , поведение не определено:
struct A { int i; }; struct B { int j; }; struct D : A, B {}; void f() { D d; static_cast<B&>(d).j; // OK, выражение объекта обозначает B подобъект d reinterpret_cast<B&>(d).j; // неопределённое поведение }
Встроенные операторы доступа к указателям на члены
Выражения оператора доступа к членам через указатели на члены имеют вид
lhs
.*
rhs
|
(1) | ||||||||
lhs
->*
rhs
|
(2) | ||||||||
T
.
T*
.
rhs
должен быть rvalue типа указатель на член (
данные
или
функцию
) класса
T
или указатель на член однозначного и доступного базового класса
B
класса
T
.
Выражение 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) , обозначающий этот член данных;
&
, программа является некорректной
если только функция-член не имеет cv-qualifier
const
но не
volatile
(начиная с C++20)
;
|
7)
если
E1
является lvalue и
E2
указывает на функцию-член с ref-qualifier
&&
, программа является некорректной.
|
(начиная с C++11) |
В
разрешении перегрузки для пользовательских операторов
, для каждой комбинации типов
D
,
B
,
R
, где классовый тип
B
является либо тем же классом, что и
D
, либо однозначным и доступным базовым классом
D
, а
R
является либо объектным, либо функциональным типом, следующая сигнатура функции участвует в разрешении перегрузки:
|
R
&
operator
-
>
*
(
D
*
, R B
::
*
)
;
|
||
где оба операнда могут быть cv-квалифицированы, и в этом случае 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); // базовые указатели работают с производными объектами D* pd = &d; std::cout << (d.*pf)(7) << ' ' << (pd->*pf)(8) << '\n'; }
Вывод:
7 14 15
Стандартная библиотека
Оператор индексирования перегружен многими стандартными классами контейнеров:
|
обращается к конкретному биту
(публичная функция-член
std::bitset<N>
)
|
|
|
предоставляет индексированный доступ к управляемому массиву
(публичная функция-член
std::unique_ptr<T,Deleter>
)
|
|
|
обращается к указанному символу
(публичная функция-член
std::basic_string<CharT,Traits,Allocator>
)
|
|
|
обращается к указанному элементу
(публичная функция-член
std::array<T,N>
)
|
|
|
обращается к указанному элементу
(публичная функция-член
std::deque<T,Allocator>
)
|
|
|
обращается к указанному элементу
(публичная функция-член
std::vector<T,Allocator>
)
|
|
|
обращается к указанному элементу или вставляет его
(публичная функция-член
std::map<Key,T,Compare,Allocator>
)
|
|
|
обращается к указанному элементу или вставляет его
(публичная функция-член
std::unordered_map<Key,T,Hash,KeyEqual,Allocator>
)
|
|
|
обращается к элементу по индексу
(публичная функция-член
std::reverse_iterator<Iter>
)
|
|
|
обращается к элементу по индексу
(публичная функция-член
std::move_iterator<Iter>
)
|
|
|
получает/устанавливает элемент valarray, срез или маску
(публичная функция-член
std::valarray<T>
)
|
|
|
возвращает указанное подсовпадение
(публичная функция-член
std::match_results<BidirIt,Alloc>
)
|
Операторы косвенного обращения и доступа к членам перегружены многими классами итераторов и умных указателей:
|
разыменовывает указатель на управляемый объект
(публичная функция-член
std::unique_ptr<T,Deleter>
)
|
|
|
разыменовывает хранимый указатель
(публичная функция-член
std::shared_ptr<T>
)
|
|
|
получает доступ к управляемому объекту
(публичная функция-член
std::auto_ptr<T>
)
|
|
|
разыменовывает итератор
(публичная функция-член
std::raw_storage_iterator<OutputIt,T>
)
|
|
|
разыменовывает декрементированный базовый итератор
(публичная функция-член
std::reverse_iterator<Iter>
)
|
|
|
пустая операция
(публичная функция-член
std::back_insert_iterator<Container>
)
|
|
|
пустая операция
(публичная функция-член
std::front_insert_iterator<Container>
)
|
|
|
пустая операция
(публичная функция-член
std::insert_iterator<Container>
)
|
|
|
получает доступ к указываемому элементу
(публичная функция-член
std::move_iterator<Iter>
)
|
|
|
возвращает текущий элемент
(публичная функция-член
std::istream_iterator<T,CharT,Traits,Distance>
)
|
|
|
пустая операция
(публичная функция-член
std::ostream_iterator<T,CharT,Traits>
)
|
|
|
получает копию текущего символа
(публичная функция-член
std::istreambuf_iterator<CharT,Traits>
)
|
|
|
пустая операция
(публичная функция-член
std::ostreambuf_iterator<CharT,Traits>
)
|
|
|
получает доступ к текущему совпадению
(публичная функция-член
std::regex_iterator<BidirIt,CharT,Traits>
)
|
|
|
получает доступ к текущей подстроке совпадения
(публичная функция-член
std::regex_token_iterator<BidirIt,CharT,Traits>
)
|
Ни одна из стандартных библиотечных классов не перегружает
operator
&
. Наиболее известным примером перегруженного
operator
&
является Microsoft COM класс
CComPtr
, хотя он также может встречаться в EDSL, таких как
boost.spirit
.
Ни одна из стандартных библиотечных классов не перегружает operator - > * . Было предложено, что это может быть частью интерфейса умных указателей , и фактически используется в этом качестве акторами в boost.phoenix , но более распространено в EDSL, таких как cpp.react .
Примечания
| Макрос тестирования возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_multidimensional_subscript
|
202110L
|
(C++23) | Многомерный оператор индексации |
Отчеты о дефектах
Следующие отчеты об изменениях в поведении, содержащие исправления дефектов, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 1213 | C++11 | индексирование rvalue массива давало lvalue | переклассифицировано как xvalue |
| CWG 1458 | C++98 |
применение
&
к lvalue неполного типа класса, который
объявляет operator & приводило к неопределенному поведению |
не указано,
какой & используется |
| CWG 1642 | C++98 | rhs во встроенных операторах доступа к указателю на член мог быть lvalue | может быть только rvalue |
| CWG 1800 | C++98 |
при применении
&
к нестатическому члену данных
анонимного объединения-члена было неясно, участвует ли анонимное объединение в результирующем типе |
анонимное объединение
не включается в результирующий тип |
| CWG 2614 | C++98 | результат E1. E2 был неясен, если E2 является ссылочным членом или перечислителем | прояснено |
| CWG 2725 | C++98 |
если
E2
является статической функцией-членом,
E1.
E2
корректно
даже если это не левый операнд operator ( ) |
E1.
E2
некорректно
в этом случае |
| CWG 2748 | C++98 |
поведение
E1
-
>
E2
было неясно, если
E1
является
нулевым указателем и E2 ссылается на статический член |
поведение является
неопределенным в этом случае |
| CWG 2813 | C++98 |
E1
не было выражением отбрасываемого значения, если
E1. E2 именует статический член или перечисление |
является |
| CWG 2823 | C++98 |
поведение
*
expr
было неясно, если
expr
не указывает на объект или функцию |
прояснено |
Смотрите также
| Общие операторы | ||||||
|---|---|---|---|---|---|---|
| присваивание |
инкремент
декремент |
арифметические | логические | сравнения |
доступ к членам
доступ |
другие |
|
a
=
b
|
++
a
|
+
a
|
!
a
|
a
==
b
|
a
[
...
]
|
вызов функции
a ( ... ) |
|
запятая
a, b |
||||||
|
условный
a ? b : c |
||||||
| Специальные операторы | ||||||
|
static_cast
преобразует один тип в другой связанный тип
|
||||||
|
C documentation
для
Member access operators
|