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

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 ] R & T :: operator [ ] ( S b ) ; N/A
a [ ... ] (desde C++23) R & T :: operator [ ] ( ... ) ;
indirección * a R & T :: operator * ( ) ; R & operator * ( T a ) ;
dirección-de & a R * T :: operator & ( ) ; R * operator & ( T a ) ;
miembro de objeto a. b No N/A N/A
miembro de puntero a - > b R * T :: operator - > ( ) ; N/A
puntero a miembro de objeto a. * b No N/A N/A
puntero a miembro de puntero a - > * b R & T :: operator - > * ( S b ) ; R & operator - > * ( T a, S b ) ;
Notas
  • Como con la mayoría de las sobrecargas definidas por el usuario, los tipos de retorno deben coincidir con los tipos de retorno proporcionados por los operadores integrados para que los operadores definidos por el usuario puedan usarse de la misma manera que los integrados. Sin embargo, en una sobrecarga de operador definida por el usuario, se puede usar cualquier tipo como tipo de retorno (incluyendo void ). Una excepción es operator - > , que debe devolver un puntero u otra clase con operator - > sobrecargado para ser realísticamente utilizable.

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)
1) Para el operador incorporado, una de las expresiones (ya sea expr1 o expr2 ) debe ser un glvalue de tipo "array de 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)
2) La forma con lista entre llaves dentro de los corchetes solo se utiliza para llamar a un operator [ ] sobrecargado.
3) La forma con lista de expresiones separadas por comas dentro de los corchetes solo se utiliza para llamar a un operator [ ] sobrecargado.

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)
1) Si el operando es una expresión lvalue de algún tipo de objeto o función 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 el operando es el nombre de una función sobrecargada, la dirección puede tomarse solo si la sobrecarga puede resolverse debido al contexto. Consulte Dirección de una función sobrecargada para más detalles.

Si expr nombra una función miembro de objeto explícito , expr debe ser un identificador calificado . Aplicar & a un identificador no calificado que nombra una función miembro de objeto explícito está mal formado.

(desde C++23)
2) Si el operando es un nombre calificado de un miembro no estático o variant que no sea una explicit object member function (desde C++23) , por ejemplo & C :: member , el resultado es un prvalue pointer to member function o pointer to data member de tipo 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)
1) La expr debe ser una expresión de tipo de clase completo T .
Si id-expr nombra un static member o un enumerator , expr es una discarded-value expression .
2) La expr debe ser una expresión de puntero a tipo de clase completo T* .
3,4) La expr debe ser una expresión de tipo escalar (ver más abajo).

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 :

1) Si E2 es un static data member :
  • Si E2 es de tipo referencia T& o T&& (since C++11) , el resultado es un lvalue de tipo T que 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 tipo T que designa ese static data member.
Esencialmente, E1 se evalúa y se descarta en ambos casos.
2) Si E2 es un miembro de datos no estático :
  • Si E2 es de tipo referencia T& o T&& (desde C++11) , el resultado es un lvalue de tipo T que 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 no es un miembro mutable , la cv-qualification del resultado es la unión de las cv-qualifications de E1 y E2 , de lo contrario (si E2 es un miembro mutable), es la unión de las volatile-qualifications de E1 y E2 .
3) Si E2 es un conjunto de sobrecarga (de una o más funciones miembro estáticas y funciones miembro no estáticas ), E1. E2 debe ser el operando izquierdo (posiblemente entre paréntesis) de un operador de llamada a función miembro , y se utiliza la resolución de sobrecarga de funciones para seleccionar la función a la que E2 se refiere, después de lo cual:
  • 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 .
4) Si E2 es un enumerador miembro, dado el tipo de E2 como T , el resultado es un rvalue (hasta C++11) un prvalue (desde C++11) de tipo T cuyo valor es el valor del enumerador.
5) Si E2 es un tipo anidado , el programa está mal formado.
6) Si E1 tiene un ScalarType y E2 es un ~ 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
La expresión de llamada a función resultante se denomina pseudo destructor call . No toma argumentos, retorna void , evalúa E1 , y finaliza el tiempo de vida de su objeto resultado. Este es el único caso donde el operando izquierdo de operator. tiene tipo no-clase. Permitir la pseudo destructor call hace posible escribir código sin tener que saber si existe un destructor para un tipo dado.

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)
1) lhs debe ser una expresión de tipo clase T .
2) lhs debe ser una expresión de tipo puntero a tipo clase 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 :

1) si E2 es un puntero a miembro de datos,
  • 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;
2) si E2 es un puntero a función miembro, el resultado es un tipo especial de prvalue que designa esa función miembro que solo puede usarse como operando izquierdo del operador de llamada a función miembro, y para ningún otro propósito;
3) Las reglas de calificación cv son las mismas que para el operador miembro de objeto, con una regla adicional: un puntero a miembro que se refiere a un miembro mutable no puede utilizarse para modificar ese miembro en un objeto const;
4) si E2 es un valor de puntero a miembro nulo, el comportamiento es indefinido;
5) si el resultado E1 es un objeto tal que su tipo no es similar al tipo de E1 , o su objeto más derivado no contiene el miembro al cual E2 se refiere, el comportamiento es indefinido;
6) si E1 es un rvalue y E2 apunta a una función miembro con calificador de referencia & , 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

Precedencia de operadores

Sobrecarga de operadores

Operadores comunes
asignación incremento
decremento
aritméticos lógicos comparación acceso a
miembros
otros

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

llamada a función

a ( ... )
coma

a, b
condicional

a ? b : c
Operadores especiales

static_cast convierte un tipo a otro tipo relacionado
dynamic_cast convierte dentro de jerarquías de herencia
const_cast añade o elimina cv -calificadores
reinterpret_cast convierte un tipo a un tipo no relacionado
Conversión estilo C convierte un tipo a otro mediante una mezcla de static_cast , const_cast , y reinterpret_cast
new crea objetos con duración de almacenamiento dinámico
delete destruye objetos previamente creados por la expresión new y libera el área de memoria obtenida
sizeof consulta el tamaño de un tipo
sizeof... consulta el tamaño de un pack (desde C++11)
typeid consulta la información de tipo de un tipo
noexcept comprueba si una expresión puede lanzar una excepción (desde C++11)
alignof consulta los requisitos de alineación de un tipo (desde C++11)

Documentación de C para Operadores de acceso a miembros