Spec-Zone.ru › C++

Операторы доступа к членам

Доступ к члену своего операнда.

Имя оператора Синтаксис Перегружаемый Примеры прототипов (для class T)
Внутри определения класса За пределами определения класса
индексация a[b]

a[...]

(с C++23)
Да R& T::operator[](S b);

R& T::operator[](...);

(с C++23)
Н/Д
косвенная адресация *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);
Примечания
  • Как и при большинстве перегрузок пользователем, типы возвращаемых значений должны соответствовать типам возвращаемых значений встроенных операторов, чтобы пользовательские операторы могли использоваться аналогично встроенным. Однако в пользовательской перегрузке оператора любой тип может быть использован как тип возвращаемого значения (включая void). Исключением является оператор ->, который должен возвращать указатель или другой класс с перегруженным оператором -> для практичного использования.

Объяснение

Встроенный оператор индексации обеспечивает доступ к объекту, на который указывает указатель или массив операнда.

Встроенный оператор косвенной адресации обеспечивает доступ к объекту или функции, на которые указывает указатель операнда.

Встроенный оператор адресации создаёт указатель, указывающий на объект или функцию операнда.

Операторы «член объекта» и «указатель на член объекта» обеспечивают доступ к члену данных или члену-функции объекта-операнда.

Встроенные операторы «член указателя» и «указатель на член указателя» обеспечивают доступ к члену данных или члену-функции класса, на который указывает указатель-операнд.

Встроенный оператор индексации

Выражения оператора индексации имеют вид

expr1 [expr2 ] (1)
expr1 [{expr , ...}] (2) (с C++11)
expr1 [expr2 , expr , ...] (3) (с C++23)
1) Для встроенного оператора одно из выражений (либо expr1, либо expr2) должно быть lvalue типа «массив T» или prvalue типа «указатель на T», а другое выражение (expr2 или expr1 соответственно) должно быть prvalue типа неименованного перечисления или целочисленного типа. Результат этого выражения имеет тип T. expr2 не может быть необработанным выражением с запятой .(с C++23)
2) Форма с фигурными скобками внутри квадратных скобок используется только для вызова перегруженного оператора operator[].
3) Форма с выражениями, разделёнными запятой, внутри квадратных скобок используется только для вызова перегруженного оператора operator[].

Встроенное выражение индексации 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"; // 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)
1) Если операнд — выражение lvalue некоторого типа объекта или функции T, то operator& создаёт и возвращает выражение prvalue типа T*, с той же квалификацией cv, которое указывает на объект или функцию, обозначенную операндом. Если у операнда неполный тип, указатель может быть сформирован, но если этот неполный тип оказывается классом, определяющим собственный operator&, не определено, будет ли использоваться встроенный или перегруженный оператор. Для операндов типа с пользовательским operator&, можно использовать std::addressof для получения истинного указателя. Обратите внимание, что в отличие от C99 и более поздних версий C, нет специального случая для унарного оператора operator&, применённого к результату унарного operator*.
Если операнд — имя перегруженной функции, адрес может быть взят только если перегрузка может быть разрешена в контексте. Подробности см. в Адресе перегруженной функции.

Если expr обозначает явную функцию-член объекта, expr должен быть квалифицированным идентификатором. Применение & к неквалифицированному идентификатору, обозначающему явную функцию-член объекта, приводит к ошибке.

(с C++23)
2) Если операнд — квалифицированное имя нестатического или вариантного члена кроме явной функции-члена объекта(с 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)
1) expr должно быть выражением полного типа класса T.
2) expr должно быть выражением указателя на полный тип класса T*.
3,4) expr должно быть выражением скалярного типа (см. ниже).

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:

1) если 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;
3) если E2 — статическая функция-член, результат — lvalue, обозначающий эту статическую функцию-член. По существу, E1 вычисляется и отбрасывается в этом случае;
4) если E2 — нестатическая функция-член, включая деструктор, результат — специальный вид prvalue, обозначающий эту нестатическую функцию-член E1 , который может быть использован только как левый операнд оператора вызова функции-члена, и ни для какой другой цели;
5) если E2 — перечислитель члена, учитывая тип E2 как T, результат — rvalue(до C++11)prvalue(с C++11) типа T, значение которого равно значению перечислителя;
6) если E2 — вложенный тип, программа некорректна;
7) если у 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)
1) lhs должно быть выражением типа класс T.
2) lhs должно быть выражением типа указатель на тип класса T*.

rhs должно быть значением правого типа указатель на член (данных или функций) T или указателем на член явного и доступного базового класса B объекта T.

Выражение E1->*E2 точно эквивалентно (*E1).*E2 для встроенных типов; поэтому следующие правила касаются только E1.*E2.

В выражении E1.*E2:

1) если E2 является указателем на данные члена,
  • если E1 является значением lvalue, результат — значение lvalue, обозначающее этот член данных,
  • в противном случае (если E1 является правым значением(до C++17)xvalue (которое может быть овеществлено из prvalue)(с C++17)), результат — правое значение(до C++11)xvalue(с C++11), обозначающее этот член данных;
2) если E2 является указателем на функцию-член, результат — особый вид prvalue, обозначающий эту функцию-член, который может использоваться только в качестве левого операнда оператора вызова функции-члена и ни для каких других целей;
3) правила cv-квалификации такие же, как и для оператора доступа к члену объекта, с одним дополнительным правилом: указатель на член, который ссылается на mutable-член, не может использоваться для модификации этого члена в объекте const;
4) если E2 — значение нулевого указателя на член, поведение не определено;
5) если результат E1 — объект, тип которого не совместим с типом E1, или его объект наивысшего уровня не содержит члена, на который указывает E2, поведение не определено;
6) если 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

Стандартная библиотека

Оператор доступа к элементу по индексу перегружен во многих классах стандартных контейнеров:

operator[]
доступ к определенному биту
(общедоступный член-функция std::bitset<N>)
operator[]
обеспечивает индексированный доступ к управляемому массиву
(общедоступный член-функция std::unique_ptr<T,Deleter>)
operator[]
доступ к указанному символу
(общедоступный член-функция std::basic_string<CharT,Traits,Allocator>)
operator[]
доступ к указанному элементу
(общедоступный член-функция std::array<T,N>)
operator[]
доступ к указанному элементу
(общедоступный член-функция std::deque<T,Allocator>)
operator[]
доступ к указанному элементу
(общедоступный член-функция std::vector<T,Allocator>)
operator[]
доступ или вставка указанного элемента
(общедоступный член-функция std::map<Key,T,Compare,Allocator>)
operator[]
доступ или вставка указанного элемента
(общедоступный член-функция std::unordered_map<Key,T,Hash,KeyEqual,Allocator>)
operator[]
доступ к элементу по индексу
(общедоступный член-функция std::reverse_iterator<Iter>)
operator[]
(C++11)
доступ к элементу по индексу
(общедоступный член-функция std::move_iterator<Iter>)
operator[]
получение/установка элемента valarray, среза или маски
(общедоступный член-функция std::valarray<T>)
operator[]
возвращает указанную подсовпадение
(общедоступный член-функция std::match_results<BidirIt,Alloc>)

Операторы косвенного обращения и членов перегружены многими итераторами и классами умных указателей:

operator*operator->
обращается к указателю на управляемый объект
(общедоступный член-функция std::unique_ptr<T,Deleter>)
operator*operator->
обращается к сохраненному указателю
(общедоступный член-функция std::shared_ptr<T>)
operator*operator->
обращается к управляемому объекту
(общедоступный член-функция std::auto_ptr<T>)
operator*
обращается к итератору
(общедоступный член-функция std::raw_storage_iterator<OutputIt,T>)
operator*operator->
обращается к декрементированному базовому итератору
(общедоступный член-функция std::reverse_iterator<Iter>)
operator*
нет операции
(общедоступный член-функция std::back_insert_iterator<Container>)
operator*
нет операции
(общедоступный член-функция std::front_insert_iterator<Container>)
operator*
нет операции
(общедоступный член-функция std::insert_iterator<Container>)
operator*operator->
(C++11)(C++11)(устарело в C++20)
обращается к элементу, на который указывает указатель
(общедоступный член-функция std::move_iterator<Iter>)
operator*operator->
возвращает текущий элемент
(общедоступный член-функция std::istream_iterator<T,CharT,Traits,Distance>)
operator*
нет операции
(общедоступный член-функция std::ostream_iterator<T,CharT,Traits>)
operator*
получает копию текущего символа
(общедоступный член-функция std::istreambuf_iterator<CharT,Traits>)
operator*
нет операции
(общедоступный член-функция std::ostreambuf_iterator<CharT,Traits>)
operator*operator->
обращается к текущему совпадению
(общедоступный член-функция std::regex_iterator<BidirIt,CharT,Traits>)
operator*operator->
обращается к текущей подсовпадению
(общедоступный член-функция 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 = b a += b a -= b a *= b a /= b a %= b a &= b a |= b a ^= b a <<= b a >>= b

++a --a a++ a--

+a -a a + b a - b a * b a / b a % b ~a a & b a | b a ^ b a << b a >> b

!a a && b a || b

a == b a != b a < b a > b a <= b a >= b a <=> b

a[...] *a &a a->b a.b a->*b a.*b

вызов функции
a(...)
запятая
a, b
условный
a ? b : c
Специальные операторы

static_cast преобразует один тип в другой связанный тип
dynamic_cast преобразует в иерархиях наследования
const_cast добавляет или удаляет cv-квалификаторы
reinterpret_cast преобразует тип в несвязанный тип
Оператор приведения в стиле C преобразует один тип в другой, комбинируя static_cast, const_cast, и reinterpret_cast
new создаёт объекты с динамическим сроком жизни
delete разрушает объекты, ранее созданные выражением new, и освобождает полученную область памяти
sizeof запрашивает размер типа
sizeof... запрашивает размер пакета параметров (с C++11)
typeid запрашивает информацию о типе типа
noexcept проверяет, может ли выражение бросить исключение (с C++11)
alignof запрашивает требования к выравниванию типа (с C++11)

C документация для Операторы доступа к членам

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/operator_member_access

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API