Namespaces
Variants

Member access operators

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Обращается к члену своего операнда.

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

Содержание

Объяснение

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

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

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

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

Встроенные операторы member of pointer и pointer to member of pointer предоставляют доступ к элементу данных или функции-члену класса, на который указывает операнд-указатель.

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

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

expr1  [ expr2  ] (1)
expr1  [{ expr  , ... }] (2) (начиная с C++11)
expr1  [ expr2  , expr  , ... ] (3) (начиная с C++23)
1) Для встроенного оператора одно из выражений (либо expr1 , либо expr2 ) должно быть glvalue типа «массив 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"; // использует версию [{...}]
}

Вывод:

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

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

(since C++23)
2) Если операнд является квалифицированным именем нестатического или variant члена кроме explicit object member function (since C++23) , например & C :: member , результатом является prvalue pointer to member function или pointer to data member типа 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)
1) Выражение expr должно быть выражением типа полного классового типа T .
Если id-expr обозначает static member или enumerator , expr является discarded-value expression .
2) Выражение expr должно быть выражением указателя на полный тип класса T* .
3,4) Выражение expr должно быть выражением скалярного типа (см. ниже).

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 :

1) Если E2 является static data member :
  • Если E2 имеет ссылочный тип T& или T&& (since C++11) , результатом является lvalue типа T , обозначающее объект или функцию, на которую ссылка указывает.
  • В противном случае, при условии что тип E2 равен T , результатом является lvalue типа T , обозначающее этот static data member.
По сути, 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 является перегруженным набором (одной или нескольких статических функций-членов и нестатических функций-членов ), E1. E2 должен быть (возможно, заключенным в скобки) левым операндом оператора вызова функции-члена , и разрешение перегрузки функций используется для выбора функции, на которую ссылается E2 , после чего:
  • Если E2 является статической функцией-членом , результатом является lvalue, обозначающее эту статическую функцию-член. По сути, E1 вычисляется и отбрасывается в этом случае.
  • В противном случае ( E2 является нестатической функцией-членом ), результатом является prvalue, обозначающее эту нестатическую функцию-член E1 .
4) Если E2 является членом-перечислением, и тип E2 задан как T , результатом будет rvalue (до C++11) prvalue (начиная с C++11) типа T , значение которого равно значению перечислителя.
5) Если E2 является вложенным типом , программа является некорректной.
6) Если E1 имеет ScalarType и E2 является ~ за которым следует имя типа или спецификатор decltype обозначающий тот же тип (без cv-квалификаторов), опционально квалифицированный , результатом является специальный вид prvalue, который может использоваться только как левый операнд оператора вызова функции и не для каких других целей
Результирующее выражение вызова функции называется псевдовызовом деструктора . Оно не принимает аргументов, возвращает void , вычисляет E1 и завершает время жизни своего результирующего объекта. Это единственный случай, когда левый операнд operator. имеет тип, не являющийся классом. Разрешение псевдовызова деструктора позволяет писать код без необходимости знать, существует ли деструктор для данного типа.

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)
1) lhs должно быть выражением типа класса T .
2) lhs должно быть выражением типа указатель на класс T* .

rhs должен быть rvalue типа указатель на член ( данные или функцию ) класса T или указатель на член однозначного и доступного базового класса B класса T .

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

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

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

где оба операнда могут быть 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 + = 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 documentation для Member access operators