Операторы доступа к членам
Доступ к члену своего операнда.
| Имя оператора | Синтаксис | Перегружаемый | Примеры прототипов (для class T) | |||||
|---|---|---|---|---|---|---|---|---|
| Внутри определения класса | За пределами определения класса | |||||||
| индексация | a[b]
| Да | R& T::operator[](S b);
| Н/Д | ||||
| косвенная адресация | *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); | ||||
Примечания
|
||||||||
Объяснение
Встроенный оператор индексации обеспечивает доступ к объекту, на который указывает указатель или массив операнда.
Встроенный оператор косвенной адресации обеспечивает доступ к объекту или функции, на которые указывает указатель операнда.
Встроенный оператор адресации создаёт указатель, указывающий на объект или функцию операнда.
Операторы «член объекта» и «указатель на член объекта» обеспечивают доступ к члену данных или члену-функции объекта-операнда.
Встроенные операторы «член указателя» и «указатель на член указателя» обеспечивают доступ к члену данных или члену-функции класса, на который указывает указатель-операнд.
Встроенный оператор индексации
Выражения оператора индексации имеют вид
expr1 [expr2 ] | (1) | |
expr1 [{expr , ...}] | (2) | (с C++11) |
expr1 [expr2 , expr , ...] | (3) | (с C++23) |
T» или prvalue типа «указатель на T», а другое выражение (expr2 или expr1 соответственно) должно быть prvalue типа неименованного перечисления или целочисленного типа. Результат этого выражения имеет тип T. expr2 не может быть необработанным выражением с запятой .(с C++23)
operator[].operator[].Встроенное выражение индексации E1[E2] идентично выражению *(E1 + E2) за исключением его категории значения (см. ниже) и порядка вычисления(с C++17): операнд-указатель (который может быть результатом преобразования «массив в указатель» и который должен указывать на элемент массива или за его пределами) корректируется для указания на другой элемент того же массива, следуя правилам арифметики указателей, а затем происходит разыменование.
При применении к массиву выражение индексации является lvalue если массив является lvalue, и xvalue, если нет(с C++11).
При применении к указателю выражение индексации всегда является lvalue.
Тип T не может быть неполным типом, даже если размер или внутренняя структура T никогда не используются, как в &x[0].
| Использование необработанного выражения с запятой в качестве второго (правого) операнда оператора индексации устарело. Например, |
(с C++20) (до C++23) |
| Необработанное выражение с запятой не может быть вторым (правым) операндом оператора индексации. Например, Для использования выражения с запятой в качестве индекса нужны скобки, например, | (с 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"; // uses the [{...}] version
}Вывод:
4242
Встроенный оператор косвенной адресации
Выражения оператора косвенной адресации имеют вид
*expr |
Операнд встроенного оператора косвенной адресации должен быть указателем на объект или функцию, а результат — lvalue, ссылающийся на объект или функцию, на которые указывает expr.
Указатель на (возможно, с квалификаторами cv) void не может быть разыменован. Указатели на другие неполные типы могут быть разыменованы, но полученное lvalue может быть использовано только в контекстах, допускающих lvalue неполного типа, например, при инициализации ссылки.
END_OF_DOCUMENT_MARKERПри разрешении перегрузки против пользовательских операторов, для каждого типа 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 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
}Встроенный оператор взятия адреса
Выражения оператора взятия адреса имеют вид
&expr | (1) | |
&class ::member | (2) |
T, то operator& создаёт и возвращает выражение prvalue типа T*, с той же квалификацией cv, которое указывает на объект или функцию, обозначенную операндом. Если у операнда неполный тип, указатель может быть сформирован, но если этот неполный тип оказывается классом, определяющим собственный operator&, не определено, будет ли использоваться встроенный или перегруженный оператор. Для операндов типа с пользовательским operator&, можно использовать std::addressof для получения истинного указателя. Обратите внимание, что в отличие от C99 и более поздних версий C, нет специального случая для унарного оператора operator&, применённого к результату унарного operator*. | Если expr обозначает явную функцию-член объекта, expr должен быть квалифицированным идентификатором. Применение | (с C++23) |
&C::member, результат — выражение prvalue указателя на функцию-член или указателя на член данных типа T в классе C. Обратите внимание, что ни &member, ни C::member, ни даже &(C::member) не могут быть использованы для инициализации указателя на член.При разрешении перегрузки против пользовательских операторов, этот оператор не вносит дополнительных сигнатур функций: встроенный оператор взятия адреса не применяется, если существует перегруженный оператор&, являющийся пригодной функцией.
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
}Встроенные операторы доступа к членам
Выражения операторов доступа к членам имеют вид
expr .template(необязательно) id-expr | (1) | |
expr ->template(необязательно) id-expr | (2) | |
expr .pseudo-destructor | (3) | |
expr ->pseudo-destructor | (4) |
T.T*.expr оценивается даже если это не обязательно (например, когда второй операнд обозначает статический член).
id-expr — имя (формально, выражение id-expression, обозначающее) члена данных или функции-члена T или недвусмысленного и доступного базового класса B T (например, E1.E2 или E1->E2), необязательно квалифицированное (например, E1.B::E2 или E1->B::E2), необязательно использующее template disambiguator (например, E1.template E2 или E1->template E2).
Если вызывается пользовательский оператор->, оператор-> вызывается снова над результирующим значением рекурсивно, пока не будет достигнут оператор->, возвращающий обычный указатель. После этого применяются встроенные семантики к этому указателю.
Выражение E1->E2 точно эквивалентно (*E1).E2 для встроенных типов; поэтому следующие правила относятся только к E1.E2.
В выражении E1.E2:
E2 — статический член данных: - если
E2имеет тип ссылкиT&илиT&&(с C++11), результат — lvalue типаTобозначающий объект или функцию, к которой привязана ссылка, - в противном случае, учитывая тип
E2какT, результат — lvalue типаTобозначающий этот статический член данных.
E1 вычисляется и отбрасывается в обоих случаях; 2) если E2 — нестатический член данных: - если
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 не является mutable членом, квалификация cv результата является объединением квалификаций cv E1 и E2, в противном случае (если E2 — mutable член), это объединение квалификаций volatile E1 и E2; E2 — статическая функция-член, результат — lvalue, обозначающий эту статическую функцию-член. По существу, E1 вычисляется и отбрасывается в этом случае;E2 — нестатическая функция-член, включая деструктор, результат — специальный вид prvalue, обозначающий эту нестатическую функцию-член E1 , который может быть использован только как левый операнд оператора вызова функции-члена, и ни для какой другой цели;E2 — перечислитель члена, учитывая тип E2 как T, результат — rvalue(до C++11)prvalue(с C++11) типа T, значение которого равно значению перечислителя;E1 есть ScalarType и E2 является ~, за которым следует имя типа или спецификатор decltype, обозначающие тот же тип (без cv-квалификаторов), необязательно квалифицированные, результат представляет собой особый вид значения prvalue, которое может использоваться только в качестве левого операнда оператора вызова функции, и ни для каких других целей. Полученное выражение вызова функции называется псевдовызовом деструктора. Оно не принимает аргументов, возвращает void, вычисляет E1, и завершает жизненный цикл объекта-результата. Это единственный случай, когда левый операнд 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.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 — объектом, тип которого не совместим с типом E1, поведение не определено:
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
}Встроенные операторы доступа к членам через указатели
Выражения оператора доступа к членам через указатели на члены имеют вид
lhs .*rhs | (1) | |
lhs ->*rhs | (2) |
T.T*.rhs должно быть значением правого типа указатель на член (данных или функций) T или указателем на член явного и доступного базового класса B объекта T.
Выражение E1->*E2 точно эквивалентно (*E1).*E2 для встроенных типов; поэтому следующие правила касаются только E1.*E2.
В выражении E1.*E2:
E2 является указателем на данные члена, - если
E1является значением lvalue, результат — значение lvalue, обозначающее этот член данных, - в противном случае (если
E1является правым значением(до C++17)xvalue (которое может быть овеществлено из prvalue)(с C++17)), результат — правое значение(до C++11)xvalue(с C++11), обозначающее этот член данных;
E2 является указателем на функцию-член, результат — особый вид prvalue, обозначающий эту функцию-член, который может использоваться только в качестве левого операнда оператора вызова функции-члена и ни для каких других целей;E2 — значение нулевого указателя на член, поведение не определено;E1 — объект, тип которого не совместим с типом E1, или его объект наивысшего уровня не содержит члена, на который указывает E2, поведение не определено;E1 — правое значение, и E2 указывает на функцию-член с квалификатором ref &, программа некорректна если функция-член имеет cv-квалификатор const, но не volatile(с C++20); 7) если E1 — значение lvalue, а E2 указывает на функцию-член с квалификатором ref &&, программа некорректна. | (с 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); // base pointers work with derived object
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>) |
|
| (C++11) | доступ к элементу по индексу (общедоступный член-функция 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>) |
|
| (C++11)(C++11)(устарело в C++20) | обращается к элементу, на который указывает указатель (общедоступный член-функция 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>) |
В стандартной библиотеке классы не перегружают оператор&. Наиболее известный пример перегрузки оператора& — класс Microsoft COM CComPtr, хотя он также может встречаться в EDSL, таких как boost.spirit.
Операторы->* не перегружены в стандартных классах библиотеки. Было предложено, что он может быть частью интерфейса умных указателей, и, фактически, используется в качестве такового актёрами в boost.phoenix, но чаще используется в EDSL, таких как cpp.react.
Примечания
| Макрос проверки на наличие функции | Значение | Std | Функция |
|---|---|---|---|
__cpp_multidimensional_subscript | 202110L | (C++23) | Многомерный оператор индексирования массива |
Отчёты об ошибках
Следующие исправляющие ошибки отчёты были применены ретроактивно к ранее опубликованным стандартам C++.
END_OF_DOCUMENT_MARKER| DR | Применимо к | Поведение по опубликованной информации | Правильное поведение |
|---|---|---|---|
| CWG 1213 | C++11 | индексирование массива rvalue приводило к lvalue | переклассифицировано как xvalue |
| CWG 1458 | C++98 | применение & к lvalue неполного класса, которыйобъявляет operator& приводило к неопределенному поведению | это не определено который & используется |
| CWG 1642 | C++98 | правая часть в встроенных операторах доступа к членам указателя может быть lvalue | может быть только rvalue |
| CWG 1800 | C++98 | при применении & к нестатическому члену данных безымянного объединения, было неясно, участвует либезымянное объединение в типе результата | безымянное объединение не включено в тип результата |
| CWG 2614 | C++98 | результат E1.E2 был неясен, если E2 является членом-ссылкой или перечислением | уточнено |
См. также
| Общие операторы | ||||||
|---|---|---|---|---|---|---|
| присваивания |
инкремент декремент | арифметические | логические | сравнения |
доступ к членам | другие |
|
|
|
|
|
|
| вызов функции |
a(...) |
||||||
| запятая | ||||||
a, b |
||||||
| условный | ||||||
a ? b : c |
||||||
| Специальные операторы | ||||||
|
|
||||||
| C документация для Операторы доступа к членам |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/operator_member_access