Namespaces
Variants

operator overloading

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

Personaliza los operadores de C++ para operandos de tipos definidos por el usuario.

Contenidos

Sintaxis

Funciones de operador son funciones con nombres de función especiales:

operator op (1)
operator new
operator new []
(2)
operator delete
operator delete []
(3)
operator co_await (4) (desde C++20)
op - cualquiera de los siguientes operadores: + - * / % ^ & | ~ ! = < > + = - = * = / = % = ^ = & = | = << >> >>= <<= == ! = <= >= <=> (desde C++20) && || ++ -- , - > * - > ( ) [ ]
1) Un operador de puntuación sobrecargado.
4) Un operador co_await sobrecargado para usar en co_await expresiones .

Los comportamientos de los operadores no de puntuación se describen en sus respectivas páginas. A menos que se especifique lo contrario, la descripción restante en esta página no se aplica a estas funciones.

Explicación

Cuando un operador aparece en una expresión , y al menos uno de sus operandos tiene un class type o un enumeration type , entonces se utiliza overload resolution para determinar la función definida por el usuario que será llamada entre todas las funciones cuyas firmas coinciden con lo siguiente:

Expresión Como función miembro Como función no miembro Ejemplo
@a (a).operator@ ( ) operator@ (a) ! std:: cin llama a std:: cin . operator ! ( )
a@b (a).operator@ (b) operator@ (a, b) std:: cout << 42 llama a std:: cout . operator << ( 42 )
a=b (a).operator= (b) no puede ser no miembro Dado std:: string s ; , s = "abc" ; llama a s. operator = ( "abc" )
a(b...) (a).operator()(b...) no puede ser no miembro Dado std:: random_device r ; , auto n = r ( ) ; llama a r. operator ( ) ( )
a[b...] (a).operator[](b...) no puede ser no miembro Dado std:: map < int , int > m ; , m [ 1 ] = 2 ; llama a m. operator [ ] ( 1 )
a-> (a).operator->( ) no puede ser no miembro Dado std:: unique_ptr < S > p ; , p - > bar ( ) llama a p. operator - > ( )
a@ (a).operator@ (0) operator@ (a, 0) Dado std:: vector < int > :: iterator i ; , i ++ llama a i. operator ++ ( 0 )

En esta tabla, @ es un marcador de posición que representa todos los operadores coincidentes: todos los operadores prefijos en @a, todos los operadores postfijos excepto -> en a@, todos los operadores infijos excepto = en a@b.

Además, para los operadores de comparación == , ! = , < , > , <= , >= , <=> , la resolución de sobrecarga también considera los candidatos reescritos operator == o operator <=> .

(desde C++20)

Los operadores sobrecargados (pero no los operadores incorporados) pueden llamarse usando notación de función:

std::string str = "Hola, ";
str.operator+=("mundo");                      // igual que str += "mundo";
operator<<(operator<<(std::cout, str), '\n'); // igual que std::cout << str << '\n';
                                              // (desde C++17) excepto por secuenciación

Operadores sobrecargados estáticos

Los operadores sobrecargados que son funciones miembro pueden declararse static . Sin embargo, esto solo está permitido para operator ( ) y operator [ ] .

Dichos operadores pueden invocarse utilizando notación de función. Sin embargo, cuando estos operadores aparecen en expresiones, aún requieren un objeto de tipo clase.

struct SwapThem
{
    template<typename T>
    static void operator()(T& lhs, T& rhs) 
    {
        std::ranges::swap(lhs, rhs);
    }
    template<typename T>
    static void operator[](T& lhs, T& rhs)
    {
        std::ranges::swap(lhs, rhs);
    } 
};
inline constexpr SwapThem swap_them{};
void foo()
{
    int a = 1, b = 2;
    swap_them(a, b); // OK
    swap_them[a, b]; // OK
    SwapThem{}(a, b); // OK
    SwapThem{}[a, b]; // OK
    SwapThem::operator()(a, b); // OK
    SwapThem::operator[](a, b); // OK
    SwapThem(a, b); // error, invalid construction
    SwapThem[a, b]; // error
}
(desde C++23)

Restricciones

  • Una función de operador debe tener al menos un parámetro de función o parámetro de objeto implícito cuyo tipo sea una clase, una referencia a una clase, una enumeración o una referencia a una enumeración.
  • Los operadores :: (resolución de ámbito), . (acceso a miembro), .* (acceso a miembro a través de puntero a miembro), y ?: (condicional ternario) no pueden sobrecargarse.
  • No se pueden crear nuevos operadores como ** , <> , o &| .
  • No es posible cambiar la precedencia, agrupación o número de operandos de los operadores.
  • La sobrecarga del operador -> debe retornar un puntero crudo, o retornar un objeto (por referencia o por valor) para el cual el operador -> esté a su vez sobrecargado.
  • Las sobrecargas de los operadores && y || pierden la evaluación de cortocircuito.
  • && , || , y , pierden sus propiedades especiales de secuenciación cuando están sobrecargados y se comportan como llamadas a funciones regulares incluso cuando se utilizan sin notación de llamada a función.
(hasta C++17)

Implementaciones canónicas

Además de las restricciones anteriores, el lenguaje no impone otras limitaciones sobre lo que hacen los operadores sobrecargados, o sobre el tipo de retorno (no participa en la resolución de sobrecarga), pero en general, se espera que los operadores sobrecargados se comporten de la manera más similar posible a los operadores integrados: operator + se espera que sume, en lugar de multiplicar sus argumentos, operator = se espera que asigne, etc. Se espera que los operadores relacionados se comporten de manera similar ( operator + y operator + = realizan la misma operación de tipo suma). Los tipos de retorno están limitados por las expresiones en las que se espera que se use el operador: por ejemplo, los operadores de asignación retornan por referencia para hacer posible escribir a = b = c = d , porque los operadores integrados permiten eso.

Los operadores comúnmente sobrecargados tienen las siguientes formas típicas y canónicas: [1]

Operador de asignación

El operador de asignación operator = tiene propiedades especiales: consulte asignación de copia y asignación de movimiento para más detalles.

Se espera que el operador de asignación por copia canónico sea seguro ante autoasignación , y que retorne la referencia al lhs:

// asignación de copia
T& operator=(const T& other)
{
    // Proteger autoasignación
    if (this == &other)
        return *this;
    // asumir que *this gestiona un recurso reutilizable, como un búfer asignado en el montón mArray
    if (size != other.size)           // el recurso en *this no puede reutilizarse
    {
        temp = new int[other.size];   // asignar recurso, si lanza excepción, no hacer nada
        delete[] mArray;              // liberar recurso en *this
        mArray = temp;
        size = other.size;
    }
    std::copy(other.mArray, other.mArray + other.size, mArray);
    return *this;
}

Se espera que la asignación de movimiento canónica deje el objeto fuente en un estado válido (es decir, un estado con las invariantes de clase intactas), y que no haga nada o al menos deje el objeto en un estado válido en caso de auto-asignación, y que retorne el lhs por referencia a no-const, y sea noexcept:

// move assignment
T& operator=(T&& other) noexcept
{
    // Guard self assignment
    if (this == &other)
        return *this; // delete[]/size=0 would also be ok
    delete[] mArray;                               // release resource in *this
    mArray = std::exchange(other.mArray, nullptr); // leave other in valid state
    size = std::exchange(other.size, 0);
    return *this;
}
(desde C++11)

En aquellas situaciones donde la asignación por copia no puede beneficiarse de la reutilización de recursos (no gestiona un array asignado en el heap y no tiene un miembro (posiblemente transitivo) que lo haga, como un miembro std::vector o std::string ), existe una abreviación conveniente popular: el operador de asignación de copia e intercambio (copy-and-swap), que toma su parámetro por valor (funcionando así tanto para asignación por copia como por movimiento dependiendo de la categoría de valor del argumento), intercambia con el parámetro, y deja que el destructor lo limpie.

// asignación de copia (idioma copy-and-swap)
T& T::operator=(T other) noexcept // llama al constructor de copia o movimiento para construir other
{
    std::swap(size, other.size); // intercambia recursos entre *this y other
    std::swap(mArray, other.mArray);
    return *this;
} // se llama al destructor de other para liberar los recursos gestionados anteriormente por *this

Este formulario proporciona automáticamente strong exception guarantee , pero prohíbe la reutilización de recursos.

Extracción e inserción de flujos

Las sobrecargas de operator>> y operator<< que toman un std:: istream & o std:: ostream & como argumento izquierdo se conocen como operadores de inserción y extracción. Dado que toman el tipo definido por el usuario como argumento derecho ( b en a @ b ), deben implementarse como no miembros.

std::ostream& operator<<(std::ostream& os, const T& obj)
{
    // escribir obj al stream
    return os;
}
std::istream& operator>>(std::istream& is, T& obj)
{
    // leer obj desde stream
    if (/* T no pudo ser construido */)
        is.setstate(std::ios::failbit);
    return is;
}

Estos operadores a veces se implementan como friend functions .

Operador de llamada a función

Cuando una clase definida por el usuario sobrecarga el operador de llamada a función operator ( ) , se convierte en un tipo FunctionObject .

Un objeto de este tipo puede utilizarse en una expresión de llamada a función:

// Un objeto de este tipo representa una función lineal de una variable a * x + b.
struct Linear
{
    double a, b;
    double operator()(double x) const
    {
        return a * x + b;
    }
};
int main()
{
    Linear f{2, 1};  // Representa la función 2x + 1.
    Linear g{-1, 0}; // Representa la función -x.
    // f y g son objetos que pueden usarse como una función.
    double f_0 = f(0);
    double f_1 = f(1);
    double g_0 = g(0);
}

Muchos algoritmos de la biblioteca estándar aceptan FunctionObject s para personalizar el comportamiento. No existen formas canónicas particularmente notables de operator ( ) , pero para ilustrar el uso:

#include <algorithm>
#include <iostream>
#include <vector>
struct Sum
{
    int sum = 0;
    void operator()(int n) { sum += n; }
};
int main()
{
    std::vector<int> v = {1, 2, 3, 4, 5};
    Sum s = std::for_each(v.begin(), v.end(), Sum());
    std::cout << "The sum is " << s.sum << '\n';
}

Salida:

The sum is 15

Incremento y decremento

Cuando el operador de incremento o decremento postfijo aparece en una expresión, se llama a la función definida por el usuario correspondiente ( operator ++ o operator -- ) con un argumento entero 0 . Normalmente, se declara como T operator ++ ( int ) o T operator -- ( int ) , donde el argumento se ignora. Los operadores de incremento y decremento postfijos generalmente se implementan en términos de las versiones prefijas:

struct X
{
    // incremento prefijo
    X& operator++()
    {
        // el incremento real ocurre aquí
        return *this; // devolver nuevo valor por referencia
    }
    // incremento postfijo
    X operator++(int)
    {
        X old = *this; // copiar valor antiguo
        operator++();  // incremento prefijo
        return old;    // devolver valor antiguo
    }
    // decremento prefijo
    X& operator--()
    {
        // el decremento real ocurre aquí
        return *this; // devolver nuevo valor por referencia
    }
    // decremento postfijo
    X operator--(int)
    {
        X old = *this; // copiar valor antiguo
        operator--();  // decremento prefijo
        return old;    // devolver valor antiguo
    }
};

Aunque las implementaciones canónicas de los operadores de incremento y decremento prefijos devuelven por referencia, como con cualquier sobrecarga de operador, el tipo de retorno es definido por el usuario; por ejemplo las sobrecargas de estos operadores para std::atomic devuelven por valor.

Operadores aritméticos binarios

Los operadores binarios normalmente se implementan como no-miembros para mantener la simetría (por ejemplo, al sumar un número complejo y un entero, si operator + es una función miembro del tipo complex, entonces solo complex + integer compilaría, y no integer + complex ). Dado que para cada operador aritmético binario existe un operador de asignación compuesta correspondiente, las formas canónicas de los operadores binarios se implementan en términos de sus asignaciones compuestas:

class X
{
public:
    X& operator+=(const X& rhs) // asignación compuesta (no necesita ser miembro,
    {                           // pero a menudo lo es, para modificar los miembros privados)
        /* la adición de rhs a *this ocurre aquí */
        return *this; // devuelve el resultado por referencia
    }
    // los amigos definidos dentro del cuerpo de la clase son inline y están ocultos de la búsqueda no-ADL
    friend X operator+(X lhs,        // pasar lhs por valor ayuda a optimizar cadenas a+b+c
                       const X& rhs) // de lo contrario, ambos parámetros pueden ser referencias constantes
    {
        lhs += rhs; // reutiliza la asignación compuesta
        return lhs; // devuelve el resultado por valor (usa el constructor de movimiento)
    }
};

Operadores de comparación

Los algoritmos de la biblioteca estándar como std::sort y los contenedores como std::set esperan que operator < esté definido, por defecto, para los tipos proporcionados por el usuario, y esperan que implemente un ordenamiento débil estricto (satisfaciendo así los requisitos de Compare ). Una forma idiomática de implementar el ordenamiento débil estricto para una estructura es usar la comparación lexicográfica proporcionada por std::tie :

struct Record
{
    std::string name;
    unsigned int floor;
    double weight;
    friend bool operator<(const Record& l, const Record& r)
    {
        return std::tie(l.name, l.floor, l.weight)
             < std::tie(r.name, r.floor, r.weight); // mantener el mismo orden
    }
};

Normalmente, una vez que se proporciona operator < , los demás operadores relacionales se implementan en términos de operator < .

inline bool operator< (const X& lhs, const X& rhs) { /* realizar comparación real */ }
inline bool operator> (const X& lhs, const X& rhs) { return rhs < lhs; }
inline bool operator<=(const X& lhs, const X& rhs) { return !(lhs > rhs); }
inline bool operator>=(const X& lhs, const X& rhs) { return !(lhs < rhs); }

Del mismo modo, el operador de desigualdad normalmente se implementa en términos de operator == :

inline bool operator==(const X& lhs, const X& rhs) { /* realizar comparación real */ }
inline bool operator!=(const X& lhs, const X& rhs) { return !(lhs == rhs); }

Cuando se proporciona una comparación de tres vías (como std::memcmp o std::string::compare ), todos los seis operadores de comparación bidireccionales pueden expresarse a través de ella:

inline bool operator==(const X& lhs, const X& rhs) { return cmp(lhs,rhs) == 0; }
inline bool operator!=(const X& lhs, const X& rhs) { return cmp(lhs,rhs) != 0; }
inline bool operator< (const X& lhs, const X& rhs) { return cmp(lhs,rhs) <  0; }
inline bool operator> (const X& lhs, const X& rhs) { return cmp(lhs,rhs) >  0; }
inline bool operator<=(const X& lhs, const X& rhs) { return cmp(lhs,rhs) <= 0; }
inline bool operator>=(const X& lhs, const X& rhs) { return cmp(lhs,rhs) >= 0; }

Operador de subíndice de array

Las clases definidas por el usuario que proporcionan acceso similar a un array permitiendo tanto lectura como escritura normalmente definen dos sobrecargas para operator [ ] : variantes const y no-const:

struct T
{
          value_t& operator[](std::size_t idx)       { return mVector[idx]; }
    const value_t& operator[](std::size_t idx) const { return mVector[idx]; }
};

Alternativamente, pueden expresarse como una única función miembro plantilla usando un parámetro de objeto explícito :

struct T
{
    decltype(auto) operator[](this auto& self, std::size_t idx) 
    { 
        return self.mVector[idx]; 
    }
};
(desde C++23)

Si se sabe que el tipo de valor es un tipo escalar, la variante const debe retornar por valor.

Cuando no se desea o no es posible el acceso directo a los elementos del contenedor, o cuando es necesario distinguir entre uso lvalue c [ i ] = v ; y rvalue v = c [ i ] ; , operator [ ] puede devolver un proxy. Consulte por ejemplo std::bitset::operator[] .

operator [ ] solo puede tomar un subíndice. Para proporcionar semántica de acceso a arreglos multidimensionales, por ejemplo, para implementar un acceso a arreglo 3D a [ i ] [ j ] [ k ] = x ; , operator [ ] debe devolver una referencia a un plano 2D, que debe tener su propio operator [ ] que devuelve una referencia a una fila 1D, que debe tener operator [ ] que devuelve una referencia al elemento. Para evitar esta complejidad, algunas bibliotecas optan por sobrecargar operator ( ) en su lugar, de modo que las expresiones de acceso 3D tengan la sintaxis similar a Fortran a ( i, j, k ) = x ; .

(hasta C++23)

operator [ ] puede tomar cualquier número de subíndices. Por ejemplo, un operator [ ] de una clase de arreglo 3D declarado como T & operator [ ] ( std:: size_t x, std:: size_t y, std:: size_t z ) ; puede acceder directamente a los elementos.

#include <array>
#include <cassert>
#include <iostream>
template<typename T, std::size_t Z, std::size_t Y, std::size_t X>
struct Array3d
{
    std::array<T, X * Y * Z> m{};
    constexpr T& operator[](std::size_t z, std::size_t y, std::size_t x) // C++23
    {
        assert(x < X and y < Y and z < Z);
        return m[z * Y * X + y * X + x];
    }
};
int main()
{
    Array3d<int, 4, 3, 2> v;
    v[3, 2, 1] = 42;
    std::cout << "v[3, 2, 1] = " << v[3, 2, 1] << '\n';
}

Salida:

v[3, 2, 1] = 42
(desde C++23)

Operadores aritméticos bit a bit

Las clases y enumeraciones definidas por el usuario que implementan los requisitos de BitmaskType deben sobrecargar los operadores aritméticos bit a bit operator & , operator | , operator ^ , operator~ , operator & = , operator | = , y operator ^ = , y opcionalmente pueden sobrecargar los operadores de desplazamiento operator << operator >> , operator >>= , y operator <<= . Las implementaciones canónicas generalmente siguen el patrón para operadores aritméticos binarios descrito anteriormente.

Operador de negación booleana

El operador operator ! comúnmente es sobrecargado por las clases definidas por el usuario que están destinadas a ser utilizadas en contextos booleanos. Dichas clases también proporcionan una función de conversión definida por el usuario al tipo booleano (ver std::basic_ios para el ejemplo de la biblioteca estándar), y el comportamiento esperado de operator ! es devolver el valor opuesto de operator bool .

(hasta C++11)

Dado que el operador incorporado ! realiza conversión contextual a bool , las clases definidas por el usuario que están destinadas a ser utilizadas en contextos booleanos podrían proporcionar únicamente operator bool y no necesitan sobrecargar operator ! .

(desde C++11)

Operadores raramente sobrecargados

Los siguientes operadores rara vez se sobrecargan:

  • El operador de dirección, operator & . Si el operador unario & se aplica a un lvalue de tipo incompleto y el tipo completo declara un operator & sobrecargado, no está especificado si el operador tiene el significado incorporado o se llama a la función operadora. Debido a que este operador puede sobrecargarse, las bibliotecas genéricas utilizan std::addressof para obtener direcciones de objetos de tipos definidos por el usuario. El ejemplo más conocido de un operator & sobrecargado canónico es la clase de Microsoft CComPtrBase . Un ejemplo del uso de este operador en EDSL se puede encontrar en boost.spirit .
  • Los operadores lógicos booleanos, operator && y operator || . A diferencia de las versiones incorporadas, las sobrecargas no pueden implementar evaluación de cortocircuito. También a diferencia de las versiones incorporadas, no secuencian su operando izquierdo antes del derecho. (hasta C++17) En la biblioteca estándar, estos operadores solo están sobrecargados para std::valarray .
  • El operador coma, operator, . A diferencia de la versión incorporada, las sobrecargas no secuencian su operando izquierdo antes del derecho. (hasta C++17) Debido a que este operador puede sobrecargarse, las bibliotecas genéricas utilizan expresiones como a, void ( ) , b en lugar de a, b para secuenciar la ejecución de expresiones de tipos definidos por el usuario. La biblioteca boost utiliza operator, en boost.assign , boost.spirit , y otras bibliotecas. La biblioteca de acceso a bases de datos SOCI también sobrecarga operator, .
  • El operador de acceso a miembro a través de puntero a miembro operator - > * . No hay desventajas específicas al sobrecargar este operador, pero rara vez se utiliza en la práctica. Se sugirió que podría ser parte de una interfaz de puntero inteligente , y de hecho se utiliza en esa capacidad por actores en boost.phoenix . Es más común en EDSLs como cpp.react .

Notas

Macro de prueba de características Valor Estándar Característica
__cpp_static_call_operator 202207L (C++23) static operator ( )
__cpp_multidimensional_subscript 202211L (C++23) static operator [ ]

Palabras clave

operator

Ejemplo

#include <iostream>
class Fraction
{
    // or C++17's std::gcd
    constexpr int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
    int n, d;
public:
    constexpr Fraction(int n, int d = 1) : n(n / gcd(n, d)), d(d / gcd(n, d)) {}
    constexpr int num() const { return n; }
    constexpr int den() const { return d; }
    constexpr Fraction& operator*=(const Fraction& rhs)
    {
        int new_n = n * rhs.n / gcd(n * rhs.n, d * rhs.d);
        d = d * rhs.d / gcd(n * rhs.n, d * rhs.d);
        n = new_n;
        return *this;
    }
};
std::ostream& operator<<(std::ostream& out, const Fraction& f)
{
   return out << f.num() << '/' << f.den();
}
constexpr bool operator==(const Fraction& lhs, const Fraction& rhs)
{
    return lhs.num() == rhs.num() && lhs.den() == rhs.den();
}
constexpr bool operator!=(const Fraction& lhs, const Fraction& rhs)
{
    return !(lhs == rhs);
}
constexpr Fraction operator*(Fraction lhs, const Fraction& rhs)
{
    return lhs *= rhs;
}
int main()
{
    constexpr Fraction f1{3, 8}, f2{1, 2}, f3{10, 2};
    std::cout << f1 << " * " << f2 << " = " << f1 * f2 << '\n'
              << f2 << " * " << f3 << " = " << f2 * f3 << '\n'
              <<  2 << " * " << f1 << " = " <<  2 * f1 << '\n';
    static_assert(f3 == f2 * 10);
}

Salida:

3/8 * 1/2 = 3/16
1/2 * 5/1 = 5/2
2 * 3/8 = 3/4

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 1481 C++98 el operador de incremento prefijo no miembro solo podía tener un parámetro
de tipo clase, tipo enumeración, o un tipo referencia a dichos tipos
sin requisito de tipo
CWG 2931 C++23 las funciones de operador miembro con objeto explícito solo podían tener ningún parámetro
de tipo clase, tipo enumeración, o un tipo referencia a dichos tipos
prohibido

Véase también

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)

Enlaces externos

  1. Operator Overloading en las Preguntas Frecuentes de C++ de StackOverflow