Other operators
|
Nombre del
operador |
Sintaxis | Sobrecargable | Ejemplos de prototipo (para class T ) | |
|---|---|---|---|---|
| Dentro de la definición de clase | Fuera de la definición de clase | |||
| llamada a función |
a(a1, a2)
|
Sí | R T :: operator ( ) ( Arg1 & a1, Arg2 & a2, ... ) ; | N/A |
| coma |
a, b
|
Sí | T2 & T :: operator , ( T2 & b ) ; | T2 & operator, ( const T & a, T2 & b ) ; |
| operador condicional |
a ? b : c
|
No | N/A | N/A |
El operador de llamada a función proporciona semántica de función para cualquier objeto.
El operador condicional (coloquialmente conocido como condicional ternario ) verifica el valor booleano de la primera expresión y, dependiendo del valor resultante, evalúa y retorna ya sea la segunda o la tercera expresión.
Contenidos |
Operador de llamada a función incorporado
Las expresiones de llamada a función tienen la siguiente forma:
función
(
arg1
,
arg2
,
arg3
,
...
)
|
|||||||||
| function | - | un tipo de función de expresión o tipo de puntero a función |
arg1
,
arg2
,
arg3
,
...
|
- | una lista posiblemente vacía de expresiones arbitrarias o listas de inicialización entre llaves (desde C++11) , excepto que el operador coma no está permitido en el nivel superior para evitar ambigüedad |
Para una llamada a una función no miembro o a una función miembro estática , function puede ser un lvalue que se refiere a una función (en cuyo caso la conversión de función a puntero se suprime), o un prvalue de tipo puntero a función.
El nombre de la función (o miembro) especificado por function puede estar sobrecargado, las reglas de resolución de sobrecarga se utilizan para decidir qué sobrecarga debe ser llamada.
Si function especifica una función miembro, puede ser virtual, en cuyo caso se llamará al final overrider de esa función, utilizando dynamic dispatch en tiempo de ejecución.
Cada parámetro de función se inicializa con su argumento correspondiente después de conversión implícita si es necesario.
- Si no hay un argumento correspondiente, se utiliza el argumento predeterminado correspondiente, y si no existe ninguno, el programa está mal formado.
- Si la llamada se realiza a una función miembro, entonces el puntero this al objeto actual se convierte como si fuera mediante una conversión explícita al puntero this esperado por la función.
- La inicialización y destrucción de cada parámetro ocurre en el contexto de la expresión completa donde aparece la llamada a la función, lo que significa, por ejemplo, que si un constructor o destructor de un parámetro lanza una excepción, los bloques try de función de la función llamada no se consideran.
Si la función es una función variádica, las promociones de argumentos predeterminadas se aplican a todos los argumentos coincidentes con el parámetro de elipsis.
Está definido por la implementación si un parámetro se destruye cuando la función en la que está definido termina o al final de la expresión completa envolvente. Los parámetros siempre se destruyen en el orden inverso de su construcción.
El tipo de retorno de una expresión de llamada a función es el tipo de retorno de la función elegida, decidido usando enlace estático (ignorando la palabra clave virtual ), incluso si la función de reemplazo que realmente se llama devuelve un tipo diferente. Esto permite que las funciones de reemplazo devuelvan punteros o referencias a clases que derivan del tipo de retorno devuelto por la función base, es decir, C++ admite tipos de retorno covariantes . Si function especifica un destructor, el tipo de retorno es void .
|
Cuando un objeto de tipo clase
El objeto temporal se construye a partir del argumento de función o valor de retorno, respectivamente, y el parámetro de función u objeto de retorno se inicializa como si se usara el constructor trivial no eliminado para copiar el temporal (incluso si ese constructor es inaccesible o no sería seleccionado por la resolución de sobrecarga para realizar una copia o movimiento del objeto). Esto permite que objetos de tipos de clase pequeños, como std::complex o std::span , se pasen a o se devuelvan desde funciones en registros. |
(since C++17) |
La categoría de valor de una expresión de llamada a función es lvalue si la función retorna una referencia lvalue o una referencia rvalue a función, es un xvalue si la función retorna una referencia rvalue a objeto, y es un prvalue en caso contrario. Si la expresión de llamada a función es un prvalue de tipo objeto, debe tener
tipo completo
excepto cuando se usa como operando de
decltype
(o como operando derecho de un
operador coma incorporado
que es el operando de
decltype
)
(desde C++11)
.
|
Cuando la función llamada finaliza normalmente, todas las
aserciones de postcondición
de la función son
evaluadas en secuencia
. Si la implementación introduce cualquier
objeto temporal
para contener el valor resultante, para la evaluación
|
(desde C++26) |
La expresión de llamada a función es similar en sintaxis a la inicialización de valor
T
(
)
, a la expresión de
conversión estilo función
T
(
A1
)
, y a la inicialización directa de un temporal
T
(
A1, A2, A3, ...
)
, donde
T
es el nombre de un tipo.
#include <cstdio> struct S { int f1(double d) { return printf("%f \n", d); // llamada a función de argumentos variables } int f2() { return f1(7); // llamada a función miembro, igual que this->f1() // argumento entero convertido a double } }; void f() { puts("function called"); // llamada a función } int main() { f(); // llamada a función S s; s.f2(); // llamada a función miembro }
Salida:
function called 7.000000
Operador coma incorporado
Las expresiones de coma tienen la siguiente forma:
E1
,
E2
|
|||||||||
En una expresión de coma
E1, E2
, la expresión
E1
es evaluada, su resultado es
descartado
(aunque si tiene tipo clase, no será destruido
hasta el final de la expresión completa contenedora
), y sus efectos secundarios se completan antes de que comience la evaluación de la expresión
E2
(nótese que un
operator,
definido por el usuario no puede garantizar la secuenciación)
(hasta C++17)
.
El tipo, valor y categoría de valor del resultado de la expresión de coma son exactamente el tipo, valor y categoría de valor del segundo operando, E2 . Si E2 es un temporal expresión (since C++17) , el resultado de la expresión es ese temporal expresión (since C++17) . Si E2 es un campo de bits, el resultado es un campo de bits.
La coma en varias listas separadas por comas, como listas de argumentos de función ( f ( a, b, c ) ) e inicializadores de lista int a [ ] = { 1 , 2 , 3 } , no es el operador coma. Si se necesita usar el operador coma en tales contextos, debe ir entre paréntesis: f ( a, ( n ++ , n + b ) , c ) .
|
El uso de 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 al usar una expresión de coma como subíndice, por ejemplo, a [ ( b, c ) ] . |
(since C++23) |
#include <iostream> int main() { // la coma se usa frecuentemente para ejecutar más de una expresión // donde la gramática del lenguaje permite solo una expresión: // * en el tercer componente del bucle for for (int i = 0, j = 10; i <= j; ++i, --j) // ^separador de lista ^operador coma std::cout << "i = " << i << " j = " << j << '\n'; // * en una sentencia return // return log("an error!"), -1; // * en una expresión de inicialización // MyClass(const Arg& arg) // : member{ throws_if_bad(arg), arg } // etc. // los operadores coma pueden encadenarse; el resultado de la última // (más a la derecha) expresión es el resultado de toda la cadena: int n = 1; int m = (++n, std::cout << "n = " << n << '\n', ++n, 2 * n); // m es ahora 6 std::cout << "m = " << (++m, m) << '\n'; }
Salida:
i = 0 j = 10 i = 1 j = 9 i = 2 j = 8 i = 3 j = 7 i = 4 j = 6 i = 5 j = 5 n = 2 m = 7
Operador condicional
Las expresiones del operador condicional tienen la forma
E1
?
E2
:
E3
|
|||||||||
E1 se evalúa y se convierte contextualmente a bool ; si el resultado es true , el resultado de la expresión condicional es el valor de E2 ; de lo contrario, el resultado de la expresión condicional es el valor de E3 .
El tipo y la categoría de valor de la expresión condicional E1 ? E2 : E3 se determinan de la siguiente manera:
Etapa 1
Si tanto E2 como E3 son de tipo void , el resultado es un rvalue (hasta C++11) un prvalue (desde C++11) de tipo void .
Si exactamente uno de E2 y E3 es de tipo void :
- Si ese operando de tipo void es una expresión throw (posiblemente entre paréntesis) , el resultado tiene el tipo y la categoría de valor del otro operando [1] . Si el otro operando es un campo de bits , el resultado también es un campo de bits.
- De lo contrario, el programa está mal formado.
Si ninguno de E2 y E3 es de tipo void , proceda a la siguiente etapa.
2 + 2 == 4 ? throw 123 : throw 456; // el resultado es de tipo “void” 2 + 2 != 4 ? "OK" : throw "error"; // el resultado es de tipo “const char[3]” // incluso si siempre se lanza una excepción
Etapa 2
Si
E2
o
E3
son
campos de bits lvalue
(hasta C++11)
campos de bits glvalue de la misma categoría de valor
(desde C++11)
y de tipos
cv1
T
y
cv2
T
, respectivamente, los operandos se consideran de tipo
cv
T
para el resto del proceso, donde
cv
es la unión de
cv1
y
cv2
.
Si E2 y E3 tienen tipos diferentes, y se satisface alguna de las siguientes condiciones, proceda a la etapa 3:
- Al menos uno de E2 y E3 es un tipo clase (posiblemente calificado-cv).
- Ambos E2 y E3 son lvalues del mismo tipo (hasta C++11) glvalues de la misma categoría de valor y el mismo tipo (desde C++11) excepto por calificación-cv.
De lo contrario, proceda a la etapa 4.
Etapa 3
Se intenta formar una
secuencia de conversión implícita
[2]
desde una expresión de operando
X
de tipo
TX
a un
tipo destino
relacionado con el tipo
TY
de la expresión de operando
Y
de la siguiente manera:
-
Si
Y
es un lvalue, el tipo destino es
TY&, pero una secuencia de conversión implícita solo puede formarse si la referencia se vincularía directamente a un lvalue (hasta C++11) un glvalue (desde C++11) .
|
(desde C++11) |
-
Si
Y
es
un rvalue
(hasta C++11)
un prvalue
(desde C++11)
o si ninguna de las secuencias de conversión anteriores puede formarse, y al menos uno de
TXyTYes un tipo clase (posiblemente calificado cv):-
Si
TXyTYson el mismo tipo de clase (ignorando calificación cv):-
Si
TYes al menos tan calificado cv comoTX, el tipo destino esTY. - De lo contrario, no se forma ninguna secuencia de conversión.
-
Si
-
De lo contrario, si
TYes una clase base deTX, el tipo destino esTYcon las calificaciones cv deTX. - De lo contrario, el tipo destino es el tipo de Z , donde Z es el valor de Y después de aplicar las conversiones estándar de lvalue-a-rvalue, array-a-puntero y función-a-puntero conversiones estándar .
-
Si
- De lo contrario, no se forma ninguna secuencia de conversión.
Mediante este proceso, se determina si se puede formar una secuencia de conversión implícita desde E2 al tipo destino determinado para E3 , y viceversa.
- Si no se puede formar ninguna secuencia de conversión, proceda a la siguiente etapa.
-
Si se puede formar exactamente una secuencia de conversión:
- Si la secuencia de conversión es ambigua, el programa está mal formado.
- De lo contrario, esa conversión se aplica al operando elegido y el operando convertido se utiliza en lugar del operando original para el proceso restante, y proceda a la siguiente etapa.
- Si ambas secuencias pueden formarse, el programa está mal formado.
struct A {}; struct B : A {}; using T = const B; A a = true ? A() : T(); // Y = A(), TY = A, X = T(), TX = const B, Objetivo = const A
Etapa 4
|
Si E2 y E3 son lvalues del mismo tipo, entonces el resultado es un lvalue de ese tipo, y es un campo de bits si al menos uno de E2 y E3 es un campo de bits. |
(hasta C++11) |
|
Si E2 y E3 son glvalues del mismo tipo y la misma categoría de valor, entonces el resultado tiene el mismo tipo y categoría de valor, y es un campo de bits si al menos uno de E2 y E3 es un campo de bits. |
(desde C++11) |
De lo contrario, el resultado es an rvalue (until C++11) a prvalue (since C++11) .
- Si E2 y E3 no tienen el mismo tipo, y alguno tiene tipo de clase (posiblemente calificado cv), proceder a la etapa 5.
- De lo contrario, proceder a la etapa 6.
Etapa 5
Resolución de sobrecarga se realiza utilizando los candidatos incorporados para intentar convertir los operandos a tipos incorporados:
- Si la resolución de sobrecarga falla, el programa está mal formado.
- De lo contrario, las conversiones seleccionadas se aplican y los operandos convertidos se utilizan en lugar de los operandos originales para el proceso restante. Proceda a la siguiente etapa.
Etapa 6
Las conversiones de array-a-puntero y función-a-puntero se aplican a (posiblemente convertidos) E2 y E3 . Después de esas conversiones, al menos una de las siguientes condiciones debe cumplirse, de lo contrario el programa está mal formado:
- E2 y E3 tienen el mismo tipo. En este caso, el resultado es de ese tipo y se inicializa por copia utilizando el operando seleccionado.
- Ambos E2 y E3 tienen tipo aritmético o de enumeración. En este caso, se aplican las conversiones aritméticas habituales para llevarlos a su tipo común, y el resultado es de ese tipo.
- Al menos uno de E2 y E3 es un puntero. En este caso, se aplican las conversiones de lvalue-a-rvalue, de puntero , puntero a función (desde C++17) y de calificación para llevarlos a su tipo de puntero compuesto , y el resultado es de ese tipo.
- Al menos uno de E2 y E3 es un puntero a miembro. En este caso, se aplican las conversiones de lvalue-a-rvalue, de puntero-a-miembro , puntero a función (desde C++17) y de calificación para llevarlos a su tipo de puntero compuesto , y el resultado es de ese tipo.
|
(desde C++11) |
int* intPtr; using Mixed = decltype(true ? nullptr : intPtr); static_assert(std::is_same_v<Mixed, int*>); // nullptr se convierte en int* struct A { int* m_ptr; } a; int* A::* memPtr = &A::m_ptr; // memPtr es un puntero al miembro m_ptr de A // memPtr hace que nullptr sea del tipo puntero al miembro m_ptr de A static_assert(std::is_same_v<decltype(false ? memPtr : nullptr), int*A::*>); // a.*memPtr es ahora solo un puntero a int y nullptr también se convierte en puntero a int static_assert(std::is_same_v<decltype(false ? a.*memPtr : nullptr), int*>);
- ↑ Dicho operador condicional se utilizaba comúnmente en programación constexpr de C++11 antes de C++14.
- ↑ Member access , si una función de conversión está eliminada (since C++11) y si un operando es un campo de bits se ignoran.
|
El tipo de resultado de un operador condicional también es accesible como el rasgo de tipo binario std::common_type . |
(desde C++11) |
Sobrecargas
Para cada par de tipos aritméticos promocionados
L
y
R
y para cada tipo
P
, donde
P
es un tipo puntero, puntero-a-miembro, o tipo de enumeración con ámbito, las siguientes firmas de función participan en la resolución de sobrecarga:
|
LR operador
?:
(
bool
, L, R
)
;
|
||
|
P operador
?:
(
bool
, P, P
)
;
|
||
donde LR es el resultado de las
conversiones aritméticas usuales
realizadas sobre
L
y
R
.
El operador “
?:
” no puede sobrecargarse, estas firmas de función existen únicamente para el propósito de resolución de sobrecarga.
#include <iostream> #include <string> struct Node { Node* next; int data; // deep-copying copy constructor Node(const Node& other) : next(other.next ? new Node(*other.next) : NULL) , data(other.data) {} Node(int d) : next(NULL), data(d) {} ~Node() { delete next; } }; int main() { // simple rvalue example int n = 1 > 2 ? 10 : 11; // 1 > 2 is false, so n = 11 // simple lvalue example int m = 10; (n == m ? n : m) = 7; // n == m is false, so m = 7 //output the result std::cout << "n = " << n << "\nm = " << m; }
Salida:
n = 11 m = 7
Biblioteca estándar
Muchas clases en la biblioteca estándar sobrecargan
operator()
para ser utilizadas como objetos función.
|
elimina el objeto o array
(función miembro pública de
std::default_delete<T>
)
|
|
|
devuelve la suma de dos argumentos
(función miembro pública de
std::plus<T>
)
|
|
|
devuelve la diferencia entre dos argumentos
(función miembro pública de
std::minus<T>
)
|
|
|
devuelve el producto de dos argumentos
(función miembro pública de
std::multiplies<T>
)
|
|
|
devuelve el resultado de la división del primer argumento por el segundo argumento
(función miembro pública de
std::divides<T>
)
|
|
|
devuelve el resto de la división del primer argumento por el segundo argumento
(función miembro pública de
std::modulus<T>
)
|
|
|
devuelve la negación del argumento
(función miembro pública de
std::negate<T>
)
|
|
|
verifica si los argumentos son iguales
(función miembro pública de
std::equal_to<T>
)
|
|
|
verifica si los argumentos no son iguales
(función miembro pública de
std::not_equal_to<T>
)
|
|
|
verifica si el primer argumento es mayor que el segundo
(función miembro pública de
std::greater<T>
)
|
|
|
verifica si el primer argumento es menor que el segundo
(función miembro pública de
std::less<T>
)
|
|
|
verifica si el primer argumento es mayor o igual que el segundo
(función miembro pública de
std::greater_equal<T>
)
|
|
|
verifica si el primer argumento es menor o igual que el segundo
(función miembro pública de
std::less_equal<T>
)
|
|
|
devuelve el AND lógico de los dos argumentos
(función miembro pública de
std::logical_and<T>
)
|
|
|
devuelve el OR lógico de los dos argumentos
(función miembro pública de
std::logical_or<T>
)
|
|
|
devuelve el NOT lógico del argumento
(función miembro pública de
std::logical_not<T>
)
|
|
|
devuelve el resultado de la operación AND a nivel de bits de dos argumentos
(función miembro pública de
std::bit_and<T>
)
|
|
|
devuelve el resultado de la operación OR bit a bit de dos argumentos
(función miembro pública de
std::bit_or<T>
)
|
|
|
devuelve el resultado de la operación XOR bit a bit de dos argumentos
(función miembro pública de
std::bit_xor<T>
)
|
|
|
devuelve el complemento lógico del resultado de una llamada al predicado almacenado
(función miembro pública de
std::unary_negate<Predicate>
)
|
|
|
devuelve el complemento lógico del resultado de una llamada al predicado almacenado
(función miembro pública de
std::binary_negate<Predicate>
)
|
|
|
llama a la función almacenada
(función miembro pública de
std::reference_wrapper<T>
)
|
|
|
invoca el objetivo
(función miembro pública de
std::function<R(Args...)>
)
|
|
|
invoca el objetivo
(función miembro pública de
std::move_only_function
)
|
|
|
invoca el objetivo
(función miembro pública de
std::copyable_function
)
|
|
|
reanuda la ejecución de la corrutina
(función miembro pública de
std::coroutine_handle<Promise>
)
|
|
|
compara lexicográficamente dos cadenas utilizando la faceta de colación de esta configuración regional
(función miembro pública de
std::locale
)
|
|
compara dos valores de tipo
value_type
(función miembro pública de
std::map<Key,T,Compare,Allocator>::value_compare
)
|
|
compara dos valores de tipo
value_type
(función miembro pública de
std::multimap<Key,T,Compare,Allocator>::value_compare
)
|
|
|
ejecuta la función
(función miembro pública de
std::packaged_task<R(Args...)>
)
|
|
|
avanza el estado del motor y devuelve el valor generado
(función miembro pública de
std::linear_congruential_engine<UIntType,a,c,m>
)
|
|
|
(C++11)
|
genera el siguiente número aleatorio en la distribución
(función miembro pública de
std::uniform_int_distribution<IntType>
)
|
El operador coma no está sobrecargado por ninguna clase en la biblioteca estándar. 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, .
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Se aplica a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 446 | C++98 |
no estaba especificado si se creaba un temporal para una
conversión de lvalue a rvalue en el operador condicional |
siempre crea un temporal si
el operador retorna un rvalue de clase |
| CWG 462 | C++98 |
si el segundo operando de un operador coma es un temporal,
no estaba especificado si su tiempo de vida se extendería cuando el resultado de la expresión coma se vincula a una referencia |
el resultado de la expresión coma
es el temporal en este caso (por lo tanto su tiempo de vida se extiende) |
| CWG 587 | C++98 |
cuando el segundo y tercer operando de un operador
condicional son lvalues del mismo tipo excepto por calificación-cv, el resultado era un lvalue si estos operandos tenían tipos clase o un rvalue en caso contrario |
el resultado es siempre
un lvalue en este caso |
| CWG 1029 | C++98 | el tipo de una llamada a destructor no estaba especificado | especificado como void |
| CWG 1550 | C++98 |
las expresiones
throw
entre paréntesis no estaban permitidas en
expresiones condicionales si el otro operando es no- void |
aceptado |
| CWG 1560 | C++98 |
void
como operando de operadores condicionales causaba
conversión gratuita de lvalue a rvalue en el otro operando, resultando siempre en rvalue |
una expresión condicional
con un void puede ser lvalue |
| CWG 1642 | C++98 |
la expresión
función
en una expresión de llamada a
función podía ser un lvalue de puntero a función |
no permitido |
| CWG 1805 | C++98 |
al determinar el tipo destino para la secuencia de conversión
implícita, la forma de convertir Y a Z no estaba clara |
aclarado |
| CWG 1895 |
C++98
C++11 |
no estaba claro si una función de conversión eliminada (C++11) o inaccesible (C++98)
previene la conversión en expresiones condicionales, y las conversiones de clase base a prvalue de clase derivada no se consideraban |
manejado como
resolución de sobrecarga |
| CWG 1932 | C++98 | faltaban campos de bits del mismo tipo en expresiones condicionales | manejado mediante tipos subyacentes |
| CWG 2226 | C++11 |
al determinar el tipo destino del otro
operando de un operador condicional, la referencia no podía vincularse a un xvalue si ese operando es un lvalue |
permitido |
| CWG 2283 | C++17 |
el requisito de completitud de tipo para el operador de llamada a
función fue eliminado accidentalmente por P0135R1 |
restaurado el requisito |
| CWG 2321 | C++98 |
al determinar el tipo destino del otro operando
de un operador condicional, un tipo de clase derivada no podía convertirse a un tipo de clase base menos calificado-cv |
permitido convertir al tipo
de clase base con la calificación-cv del operando de clase derivada |
| CWG 2715 | C++98 |
la inicialización y destrucción de cada
parámetro ocurriría dentro del contexto de la función llamante, que podría no existir [1] |
ocurre dentro del contexto de
la expresión completa envolvente |
| CWG 2850 | C++98 | el orden de destrucción de parámetros no estaba claro | aclarado |
| CWG 2865 | C++98 |
si
TX
y
TY
son el mismo tipo de clase y
TX
está
más calificado-cv que
TY
, aún podría formarse una secuencia
de conversión implícita desde un prvalue Y |
no se formará secuencia de conversión
en este caso |
| CWG 2906 | C++98 |
las conversiones de lvalue a rvalue se aplicaban incondicionalmente
en el caso de resultado rvalue para el operador condicional |
solo aplicado en algunos casos |
- ↑ Por ejemplo, las funciones pueden ser llamadas en el inicializador de una variable de ámbito de namespace, no existe una "función llamadora" en este contexto.
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
|
+
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
Otros operadores
|