Member access operators
Accede a un miembro de su operando.
| Nombre del operador | Sintaxis | Sobrecar gable | Ejemplos de prototipo (para class T ) | |
|---|---|---|---|---|
| Dentro de la definición de clase | Fuera de la definición de clase | |||
| subíndice | a [ b ] | Sí | R & T :: operator [ ] ( S b ) ; | N/A |
| a [ ... ] (desde C++23) | R & T :: operator [ ] ( ... ) ; | |||
| indirección | * a | Sí | R & T :: operator * ( ) ; | R & operator * ( T a ) ; |
| dirección-de | & a | Sí | R * T :: operator & ( ) ; | R * operator & ( T a ) ; |
| miembro de objeto | a. b | No | N/A | N/A |
| miembro de puntero | a - > b | Sí | R * T :: operator - > ( ) ; | N/A |
| puntero a miembro de objeto | a. * b | No | N/A | N/A |
| puntero a miembro de puntero | a - > * b | Sí | R & T :: operator - > * ( S b ) ; | R & operator - > * ( T a, S b ) ; |
|
||||
Contenidos |
Explicación
El operador de subíndice incorporado proporciona acceso al objeto apuntado por el operando puntero o array .
El operador de indirección incorporado proporciona acceso a un objeto o función apuntado por el operando puntero.
Operador incorporado de dirección-de crea un puntero que apunta al objeto o función operando.
Operador de miembro de objeto y operador de puntero a miembro de objeto proporcionan acceso a un miembro de datos o función miembro del operando objeto.
Los operadores integrados miembro de puntero y puntero a miembro de puntero proporcionan acceso a un miembro de datos o función miembro de la clase apuntada por el operando puntero.
Operador de subíndice incorporado
Las expresiones del operador de subíndice tienen la forma
expr1
[
expr2
]
|
(1) | ||||||||
expr1
[{
expr
, ...
}]
|
(2) | (desde C++11) | |||||||
expr1
[
expr2
,
expr
, ...
]
|
(3) | (desde C++23) | |||||||
T
" o un prvalue de tipo "puntero a
T
", mientras que la otra expresión (
expr2
o
expr1
, respectivamente) debe ser un prvalue de tipo enumeración sin ámbito o tipo integral. El resultado de esta expresión tiene el tipo
T
.
expr2
no puede ser una
expresión coma
sin paréntesis.
(desde C++23)
La expresión de subíndice incorporada E1 [ E2 ] es exactamente idéntica a la expresión * ( E1 + E2 ) excepto por su categoría de valor (ver más abajo) y el orden de evaluación (desde C++17) : el operando puntero (que puede ser resultado de una conversión de array a puntero, y debe apuntar a un elemento de algún array o una posición después del final) se ajusta para apuntar a otro elemento del mismo array, siguiendo las reglas de la aritmética de punteros , y luego se desreferencia.
Cuando se aplica a un array, la expresión de subíndice es un lvalue si el array es un lvalue, y un xvalue si no lo es (since C++11) .
Cuando se aplica a un puntero, la expresión de subíndice siempre es un lvalue.
El tipo
T
no puede ser un
tipo incompleto
, incluso si el tamaño o la estructura interna de
T
nunca se utiliza, como en
&
x
[
0
]
.
|
Usar una expresión de coma sin paréntesis como segundo argumento (derecho) de un operador de subíndice está obsoleto. Por ejemplo, a [ b, c ] está obsoleto y a [ ( b, c ) ] no lo está. |
(since C++20)
(until C++23) |
|
Una expresión de coma sin paréntesis no puede ser el segundo argumento (derecho) de un operador de subíndice. Por ejemplo, a [ b, c ] es incorrecto o equivalente a a. operator [ ] ( b, c ) . Se necesitan paréntesis para usar una expresión de coma como subíndice, ej., a [ ( b, c ) ] . |
(since C++23) |
En
la resolución de sobrecarga frente a operadores definidos por el usuario
, para cada tipo de objeto
T
(posiblemente calificado cv), la siguiente firma de función participa en la resolución de sobrecarga:
|
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"; // utiliza la versión [{...}] }
Salida:
4242
Operador de indirección incorporado
Las expresiones con el operador de indirección tienen la forma
*
expr
|
|||||||||
El operando del operador de indirección incorporado debe ser un puntero a objeto o un puntero a función, y el resultado es el lvalue que se refiere al objeto o función al que
expr
apunta. Si
expr
no apunta realmente a un objeto o función, el comportamiento es indefinido (excepto para el caso especificado por
typeid
).
Un puntero a (posiblemente cv -calificado) void no puede ser desreferenciado. Los punteros a otros tipos incompletos pueden ser desreferenciados, pero el lvalue resultante solo puede usarse en contextos que permitan un lvalue de tipo incompleto, por ejemplo, al inicializar una referencia.
En
la resolución de sobrecarga contra operadores definidos por el usuario
, para cada tipo
T
que sea tipo objeto (posiblemente calificado cv) o tipo función (no calificado const o ref), la siguiente firma de función participa en la resolución de sobrecarga:
|
T
&
operator
*
(
T
*
)
;
|
||
#include <iostream> int f() { return 42; } int main() { int n = 1; int* pn = &n; int& r = *pn; // un lvalue puede vincularse a una referencia int m = *pn; // indirección + conversión lvalue-a-rvalue int (*fp)() = &f; int (&fr)() = *fp; // un lvalue de función puede vincularse a una referencia [](...){}(r, m, fr); // elimina posibles advertencias de "variable no utilizada" }
Operador de dirección incorporado
Las expresiones del operador de dirección tienen la forma
&
expr
|
(1) | ||||||||
&
class
::
member
|
(2) | ||||||||
T
,
operator&
crea y retorna un prvalue de tipo
T*
, con la misma calificación cv, que apunta al objeto o función designado por el operando. Si el operando tiene tipo incompleto, el puntero puede formarse, pero si ese tipo incompleto resulta ser una clase que define su propio
operator
&
, no está especificado si se utiliza el operador incorporado o la sobrecarga. Para los operandos de tipo con
operator
&
definido por el usuario,
std::addressof
puede utilizarse para obtener el puntero verdadero.
Nótese que, a diferencia de C99 y versiones posteriores de C, no hay un caso especial para el operador unario
operator
&
aplicado al resultado del operador unario
operator
*
.
|
Si
expr
nombra una
función miembro de objeto explícito
,
expr
debe ser un
identificador calificado
. Aplicar
|
(desde C++23) |
T
en la clase
C
. Nótese que ni
&
member
ni
C
::
member
ni siquiera
&
(
C
::
member
)
pueden usarse para inicializar un pointer to member.
En la resolución de sobrecarga frente a operadores definidos por el usuario , este operador no introduce ninguna firma de función adicional: el operador de dirección incorporado no se aplica si existe un operator & sobrecargado que sea una función viable .
void f(int) {} void f(double) {} struct A { int i; }; struct B { void f(); }; int main() { int n = 1; int* pn = &n; // puntero int* pn2 = &*pn; // pn2 == pn int A::* mp = &A::i; // puntero a miembro de datos void (B::*mpf)() = &B::f; // puntero a función miembro void (*pf)(int) = &f; // resolución de sobrecarga por contexto de inicialización // auto pf2 = &f; // error: tipo de función sobrecargada ambiguo auto pf2 = static_cast<void (*)(int)>(&f); // resolución de sobrecarga por conversión }
Operadores de acceso a miembros incorporados
Las expresiones de operador de acceso a miembro tienen la forma
expr
.template
(opcional)
id-expr
|
(1) | ||||||||
expr
->template
(opcional)
id-expr
|
(2) | ||||||||
expr
.
pseudo-destructor
|
(3) | ||||||||
expr
->
pseudo-destructor
|
(4) | ||||||||
T*
.
id-expr
es el nombre de (formalmente, una
expresión de identificador
que nombra) un miembro de datos o función miembro de
T
o de una clase base no ambigua y accesible
B
de
T
(por ejemplo,
E1.
E2
o
E1
-
>
E2
), opcionalmente
calificado
(por ejemplo,
E1.
B
::
E2
o
E1
-
>
B
::
E2
), opcionalmente usando el
template
desambiguador
(por ejemplo,
E1.
template
E2
o
E1
-
>
template
E2
).
Si se llama a un operator - > definido por el usuario, operator - > se llama nuevamente sobre el valor resultante, recursivamente, hasta que se alcanza un operator - > que retorna un puntero simple. Después de eso, se aplica la semántica incorporada a ese puntero.
La expresión E1 - > E2 es exactamente equivalente a ( * E1 ) . E2 para tipos incorporados; por eso las siguientes reglas solo abordan E1. E2 .
En la expresión E1. E2 :
-
Si
E2
es de tipo referencia
T&oT&&(since C++11) , el resultado es un lvalue de tipoTque designa el objeto o función al cual la referencia está vinculada. -
En caso contrario, dado el tipo de
E2
como
T, el resultado es un lvalue de tipoTque designa ese static data member.
-
Si
E2
es de tipo referencia
T&oT&&(desde C++11) , el resultado es un lvalue de tipoTque designa el objeto o función al cual está vinculado el correspondiente miembro de referencia de E1 . - En caso contrario, si E1 es un lvalue, el resultado es un lvalue que designa ese miembro de datos no estático de E1 .
- En caso contrario (si E1 es un rvalue (hasta C++17) xvalue (que puede ser materializado desde prvalue) (desde C++17) ), el resultado es un rvalue (hasta C++11) xvalue (desde C++11) que designa ese miembro de datos no estático de E1 .
- Si E2 es una función miembro estática , el resultado es un lvalue que designa esa función miembro estática. Esencialmente, E1 se evalúa y se descarta en este caso.
- En caso contrario ( E2 es una función miembro no estática ), el resultado es un prvalue que designa esa función miembro no estática de E1 .
T
, el resultado es
un rvalue
(hasta C++11)
un prvalue
(desde C++11)
de tipo
T
cuyo valor es el valor del enumerador.
~
seguido por el
nombre de tipo
o
especificador decltype
que designa el mismo tipo (menos calificaciones cv), opcionalmente
calificado
, el resultado es un tipo especial de prvalue que solo puede usarse como operando izquierdo de un operador de llamada a función, y para ningún otro propósito
operator. no se puede sobrecargar, y para operator - > , en la resolución de sobrecarga frente a operadores definidos por el usuario , el operador incorporado no introduce ninguna firma de función adicional: el operator - > incorporado no se aplica si existe un operator - > sobrecargado que sea una función viable .
#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 también funciona << a.f() << ' ' << a.sf() << ' ' // A::sf() también funciona // << &a.f << ' ' // error: mal formado si a.f no es el // operando izquierdo de operator() // << a.B << ' ' // error: tipo anidado no permitido << a.RED << ' '; // enumerador UPtrWrapper uPtrWrap{std::make_unique<std::string>("wrapped")}; assert(uPtrWrap->data() == uPtrWrap.operator->().operator->()->data()); }
Salida:
1 2 11 4 1
Si E2 es un miembro no estático y el resultado de E1 es un objeto cuyo tipo no es similar al tipo de E1 , el comportamiento es indefinido:
struct A { int i; }; struct B { int j; }; struct D : A, B {}; void f() { D d; static_cast<B&>(d).j; // OK, la expresión de objeto designa el subobjeto B de d reinterpret_cast<B&>(d).j; // comportamiento indefinido }
Operadores integrados de acceso a puntero a miembro
Las expresiones de operador de acceso a miembros a través de punteros a miembros tienen la forma
lhs
.*
rhs
|
(1) | ||||||||
lhs
->*
rhs
|
(2) | ||||||||
T
.
T*
.
rhs
debe ser un rvalue de tipo puntero a miembro (
data
o
function
) de
T
o puntero a miembro de una clase base
B
de
T
que sea unívoca y accesible.
La expresión E1 - > * E2 es exactamente equivalente a ( * E1 ) . * E2 para tipos incorporados; por eso las siguientes reglas abordan únicamente E1. * E2 .
En la expresión E1. * E2 :
- si E1 es un lvalue, el resultado es un lvalue que designa ese miembro de datos,
- en caso contrario (si E1 es un rvalue (hasta C++17) xvalue (que puede ser materializado desde prvalue) (desde C++17) ), el resultado es un rvalue (hasta C++11) xvalue (desde C++11) que designa ese miembro de datos;
&
, el programa está mal formado
a menos que la función miembro tenga el calificador cv
const
pero no
volatile
(desde C++20)
;
|
7)
si
E1
es un lvalue y
E2
apunta a una función miembro con calificador de referencia
&&
, el programa está mal formado.
|
(since C++11) |
En
la resolución de sobrecarga frente a operadores definidos por el usuario
, para cada combinación de tipos
D
,
B
,
R
, donde el tipo de clase
B
es la misma clase que
D
o una clase base unívoca y accesible de
D
, y
R
es un tipo de objeto o función, la siguiente firma de función participa en la resolución de sobrecarga:
|
R
&
operator
-
>
*
(
D
*
, R B
::
*
)
;
|
||
donde ambos operandos pueden estar calificados con cv, en cuyo caso la calificación cv del tipo de retorno es la unión de las calificaciones cv de los operandos.
#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); // los punteros base funcionan con objetos derivados D* pd = &d; std::cout << (d.*pf)(7) << ' ' << (pd->*pf)(8) << '\n'; }
Salida:
7 14 15
Biblioteca estándar
El operador de subíndice está sobrecargado por muchas clases de contenedores estándar:
|
accede a un bit específico
(función miembro pública de
std::bitset<N>
)
|
|
|
proporciona acceso indexado al array gestionado
(función miembro pública de
std::unique_ptr<T,Deleter>
)
|
|
|
accede al carácter especificado
(función miembro pública de
std::basic_string<CharT,Traits,Allocator>
)
|
|
|
accede al elemento especificado
(función miembro pública de
std::array<T,N>
)
|
|
|
accede al elemento especificado
(función miembro pública de
std::deque<T,Allocator>
)
|
|
|
accede al elemento especificado
(función miembro pública de
std::vector<T,Allocator>
)
|
|
|
accede o inserta el elemento especificado
(función miembro pública de
std::map<Key,T,Compare,Allocator>
)
|
|
|
accede o inserta el elemento especificado
(función miembro pública de
std::unordered_map<Key,T,Hash,KeyEqual,Allocator>
)
|
|
|
accede a un elemento por índice
(función miembro pública de
std::reverse_iterator<Iter>
)
|
|
|
accede a un elemento por índice
(función miembro pública de
std::move_iterator<Iter>
)
|
|
|
obtiene/establece elemento, segmento o máscara de valarray
(función miembro pública de
std::valarray<T>
)
|
|
|
devuelve la subcoincidencia especificada
(función miembro pública de
std::match_results<BidirIt,Alloc>
)
|
Los operadores de indirección y miembro están sobrecargados por muchos iteradores y clases de punteros inteligentes:
|
desreferencia el puntero al objeto gestionado
(función miembro pública de
std::unique_ptr<T,Deleter>
)
|
|
|
desreferencia el puntero almacenado
(función miembro pública de
std::shared_ptr<T>
)
|
|
|
accede al objeto gestionado
(función miembro pública de
std::auto_ptr<T>
)
|
|
|
desreferencia el iterador
(función miembro pública de
std::raw_storage_iterator<OutputIt,T>
)
|
|
|
desreferencia el iterador subyacente decrementado
(función miembro pública de
std::reverse_iterator<Iter>
)
|
|
|
operación nula
(función miembro pública de
std::back_insert_iterator<Container>
)
|
|
|
operación nula
(función miembro pública de
std::front_insert_iterator<Container>
)
|
|
|
operación nula
(función miembro pública de
std::insert_iterator<Container>
)
|
|
|
accede al elemento apuntado
(función miembro pública de
std::move_iterator<Iter>
)
|
|
|
devuelve el elemento actual
(función miembro pública de
std::istream_iterator<T,CharT,Traits,Distance>
)
|
|
|
operación nula
(función miembro pública de
std::ostream_iterator<T,CharT,Traits>
)
|
|
|
obtiene una copia del carácter actual
(función miembro pública de
std::istreambuf_iterator<CharT,Traits>
)
|
|
|
operación nula
(función miembro pública de
std::ostreambuf_iterator<CharT,Traits>
)
|
|
|
accede a la coincidencia actual
(función miembro pública de
std::regex_iterator<BidirIt,CharT,Traits>
)
|
|
|
accede a la subcoincidencia actual
(función miembro pública de
std::regex_token_iterator<BidirIt,CharT,Traits>
)
|
Ninguna clase de la biblioteca estándar sobrecarga
operator
&
. El ejemplo más conocido de
operator
&
sobrecargado es la clase Microsoft COM
CComPtr
, aunque también puede aparecer en EDSLs como
boost.spirit
.
Ninguna clase de la biblioteca estándar sobrecarga operator - > * . Se sugirió que podría ser parte de la interfaz de punteros inteligentes , y de hecho se utiliza en esa capacidad por actores en boost.phoenix , pero es más común en EDSLs como cpp.react .
Notas
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_multidimensional_subscript
|
202110L
|
(C++23) | Operador de subíndice multidimensional |
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 1213 | C++11 | la indexación de un valor r de array resultaba en un valor l | reclasificado como valor x |
| CWG 1458 | C++98 |
aplicar
&
a un valor l de tipo de clase incompleto que
declara operator & resultaba en comportamiento indefinido |
no está especificado
cuál & se utiliza |
| CWG 1642 | C++98 | el rhs en los operadores de acceso a miembro de puntero integrados podía ser un valor l | solo puede ser un valor r |
| CWG 1800 | C++98 |
al aplicar
&
a un miembro de datos no estático de una
unión anónima miembro, no estaba claro si la unión anónima participaba en el tipo resultante |
la unión anónima
no se incluye en el tipo resultante |
| CWG 2614 | C++98 | el resultado de E1. E2 no estaba claro si E2 es un miembro referencia o enumerador | se aclaró |
| CWG 2725 | C++98 |
si
E2
es una función miembro estática,
E1.
E2
está bien formado
incluso si no es el operando izquierdo de operator ( ) |
E1.
E2
está mal formado
en este caso |
| CWG 2748 | C++98 |
el comportamiento de
E1
-
>
E2
no estaba claro si
E1
es un
puntero nulo y E2 se refiere a un miembro estático |
el comportamiento es
indefinido en este caso |
| CWG 2813 | C++98 |
E1
no era una expresión de valor descartado si
E1. E2 nombra un miembro estático o enumeración |
lo es |
| CWG 2823 | C++98 |
el comportamiento de
*
expr
no estaba claro si
expr
no apunta a un objeto o función |
se aclaró |
Véase también
| Operadores comunes | ||||||
|---|---|---|---|---|---|---|
| asignación |
incremento
decremento |
aritméticos | lógicos | comparación |
acceso a
miembros |
otros |
|
a
=
b
|
++
a
|
+
a
|
!
a
|
a
==
b
|
a
[
...
]
|
llamada a función
a ( ... ) |
|
coma
a, b |
||||||
|
condicional
a ? b : c |
||||||
| Operadores especiales | ||||||
|
static_cast
convierte un tipo a otro tipo relacionado
|
||||||
|
Documentación de C
para
Operadores de acceso a miembros
|