¿`New` en` new int; `se considera un operador?

84

La expresión new int;como en int * x = new int;es una nueva expresión . El término "nuevo operador" parece usarse indistintamente con "nueva expresión", por ejemplo en esta pregunta: ¿ Diferencia entre 'nuevo operador' y 'operador nuevo'?

¿Es correcto decir que la palabra clave newtal como se usa en una nueva expresión es un operador? ¿Por qué o por qué no?

Si no es así, ¿existe otra justificación para llamar a una nueva expresión "nuevo operador"?

Tengo problemas para encontrar una definición autorizada de lo que constituye un operador.

Ya entiendo la distinción entre el operador nuevo que asigna memoria para un objeto y "nueva expresión" que eventualmente puede llamar a un operator new.

François Andrieux
fuente
4
@Rogue - La cosa es, newes como sizeofaquí. Y creo que la mayoría de la comunidad de C ++ piensa sizeofen un operador. El stansard incluso se refiere al " sizeofoperador" cuando describe sus efectos.
StoryTeller - Unslander Monica
2
@ StoryTeller-UnslanderMonica, continuando desde mi segundo comentario (ahora movido a una respuesta), con sizeof, yo diría que sería el operador de una expresión sizeof(<type>). Y +es un operador para una expresión unaria <type> <op> <type>. Esa es mi conclusión / interpretación al menos, siéntete libre de corregirme.
Rogue
2
@Rogue - De hecho. Es por eso que diría que el texto normativo es bastante arbitrario cuando se refiere a uno como operador y no al otro.
StoryTeller - Unslander Monica
1
Una nota al margen de la respuesta aceptada: independientemente de si newes un operador o no, hay una diferencia entre una expresión y un operador. Por ejemplo, new intes una expresión que se evalúa como algo. Si newse considera un operador, entonces sólo la parte "nueva" de new intes el operador (es decir, es su propia cosa separada). Conceptualmente, un operador es algo que actúa sobre otra cosa.
Filip Milovanović

Respuestas:

140

newin new intno se considera un operador. Tampoco se considera que no sea un operador.

El estándar C ++ es realmente vago, e incluso inconsistente, sobre lo que constituye un 'operador'. Cuando se enumeran los operadores (como se define durante el lexing y el preprocesamiento), los enumera junto con los "puntuadores" (cosas como (), pero nunca da realmente ninguna regla para los puntuadores en general. Se enumera newcomo palabra clave y como operador. Se enumera sizeofen el conjunto de palabras clave pero NO en el conjunto de operadores, pero luego se refiere a él como un operador.

La conclusión aquí es que el comité de estándares de C ++ no está demasiado preocupado por separar el mundo léxico en "operadores" y "no operadores". Esto se debe a que realmente no es necesario. No hay reglas gramaticales que se apliquen a todos los operadores o no a todos los operadores. Los conjuntos importantes, como el conjunto de operadores sobrecargables , se proporcionan por separado; Las reglas gramaticales para cosas como expresiones aritméticas binarias se dan una a la vez.

Básicamente, "operador" no es una categoría que sea formalmente significativa para el lenguaje C ++, por lo que cualquier respuesta categórica se basará en cómo se "siente". Puede llamarlo operador si lo desea, pero también puede llamarlo una palabra clave (¡o un puntuador!) Y el estándar de idioma no estará en desacuerdo con usted.

Sneftel
fuente
6
Tanto los Estándares C como C ++ son descuidados con respecto a gran parte de su terminología, ya que esperaban que los lectores infirieran lo que se quería decir si había lagunas o ambigüedades, en lugar de tratar de discutir si podrían usar tales omisiones para justificar "optimizaciones "en los casos en que los autores de las Normas hubieran considerado suficientemente absurdo que no había necesidad de prohibirlas expresamente.
supercat
El estándar puede ser vago en las descripciones humanas de esa manera, pero existe la palabra clave operatory me imagino que, cuando alguien pide una delimitación exacta, es justo usar la palabra en inglés "operador" para las cosas que funcionan con esa palabra clave. En ese sentido, new, new[], delete, delete[], co_await, algunos typecasts, y conversiones de literales definidos por el usuario a los objetos serían operadores.
Hemflit
Los operadores @hemflit son anteriores a la operatorpalabra clave e incluyen cosas como .que no se pueden usar con la operatorpalabra clave. Si me vieran forzado a punta de pistola a dar una definición de "operador", mi mente no iría a "operador sobrecargable".
Sneftel
El uso de @Sneftel de la palabra inglesa ambigua "operator" es anterior a la operatorpalabra clave, sí. Si me viera obligado a encontrar una delimitación absolutamente precisa de lo que es o no un operador, optaría por lo único que está definido con precisión en la gramática. En la comunicación diaria, no necesitaría tanta precisión todo el tiempo.
Hemflit
Concedo fácilmente que es una definición precisa. Simplemente no es bueno.
Sneftel
16

¿Es correcto decir que la palabra clave newtal como se usa en una nueva expresión es un operador? ¿Por qué o por qué no?

No. En newuna nueva-expresión es una palabra clave que identifica una nueva-expresión .

Una nueva expresión llama una operator newo operator new[]para obtener almacenamiento. También inicializa ese almacenamiento y lo desasigna (con operator deleteo operator delete[]) si la inicialización arroja.

Hay una clara distinción, ya que operator newsolo se la conoce como una función reemplazable por el usuario sobrecargable , y una nueva expresión hace más que simplemente llamar a esta función.

Referencia: 7.6.2.8/10 [expr.new]

Una nueva expresión puede obtener almacenamiento para el objeto llamando a una función de asignación ( [basic.stc.dynamic.allocation]). Si la nueva expresión termina lanzando una excepción, puede liberar almacenamiento llamando a una función de desasignación. Si el tipo asignado es un tipo que no es de matriz, el nombre de la función de asignación es operator newy el nombre de la función de desasignación es operator delete. Si el tipo asignado es un tipo de matriz, el nombre de la función de asignación es operator new[]y el nombre de la función de desasignación es operator delete[].


Considere, a modo de contraejemplo, que definimos tanto

T operator+(T, T);
void* T::operator new(std::size_t);

para algún tipo T, luego suma en cualquiera de las formas:

T a, b;
T c = a + b;
T d = T::operator +(a, b);

es idéntico. La notación infija es simplemente azúcar sintáctica para la llamada del operador.

Sin embargo, la asignación se comporta de manera muy diferente:

T *p = new T;
// does much more than
void *v = T::operator new(sizeof(T));

por lo que no es razonable llamar al azúcar sintáctico de nueva expresión para llamar a operator new. Por lo tanto, la newpalabra clave no es simplemente seleccionar la función a llamar. No puede ser, o tendría que mencionar la operator deletefunción que también se podría llamar.

Inútil
fuente
2
" operator newEs un operador sobrecargable", ¿verdad? Yo no lo creo: operator + no es un operador sobrecargable - +es, y que defino sobrecargas por medio de una función de llamada operator +.
Konrad Rudolph
1
No, respondí señalando que el operador es una función específica llamada como parte de la nueva expresión. Una nueva-expresión es una expresión identificada por una palabra clave y encuentra un operador adecuado para llamar.
Inútil
1
Todavía no veo cómo se aborda el estado de la newpalabra clave tal como aparece en una nueva expresión.
John Bollinger
6
De hecho, el hecho de que newesté sujeto a la sobrecarga del operador sugiere que la respuesta debería ser , new(en sí mismo) es un operador.
John Bollinger
2
El estándar se refiere solo a una expresión que también llama a un operador. No pueden ser lo mismo sin que sea recursivo, por lo que la interpretación parsimoniosa es que la expresión y el operador son exactamente y solo lo que el estándar dice que son.
Inútil
12

Solo lo llamaría nueva expresión para evitar confusiones con lo void* operator new ( std::size_t count )que solo asigna memoria como parte del proceso que factura la nueva expresión (memoria de asignación, vida útil inicial, constructor de llamada).

El problema con newes que hace más que simplemente llamar al operator new. Lo cual es un poco confuso porque es para x + ylas +únicas llamadas operator +.

t.niese
fuente
9

Esto se rige por [expr.new] , que diferencia claramente entre una nueva expresión y cómo una nueva expresión puede obtener almacenamiento mediante la llamada a una función de asignación , que se nombra operator new. En particular, de [expr.new] / 8 [ énfasis mío]:

Una nueva expresión puede obtener almacenamiento para el objeto llamando a una función de asignación ([basic.stc.dynamic.allocation]). Si la nueva expresión termina lanzando una excepción, puede liberar almacenamiento llamando a una función de desasignación. Si el tipo asignado es un tipo que no es de matriz, el nombre de la función de asignación esoperator new y el nombre de la función de desasignación es operator delete. Si el tipo asignado es un tipo de matriz, el nombre de la función de asignación es operadornew[] y el nombre de la función de desasignación es operador delete[].


¿Es correcto decir que la palabra clave newtal como se usa en una nueva expresión es un operador?

En particular, el ejemplo, aunque no normativo, de [expr.new] / 4 describe esta función como una función de operador; "[...] el newoperador" :

[...] En su lugar, se puede utilizar la versión explícitamente entre paréntesis del nuevo operador [...]

dfrib
fuente
3
Gracias por la respuesta. Por la cantidad de respuestas que he recibido, parece que puede que no haya un lenguaje estricto que responda a esta pregunta, de una forma u otra. Entonces, el texto no normativo podría terminar siendo la mejor evidencia.
François Andrieux
8

No.

Bueno, algo sí, en el sentido de que existen personas que se consideran newen new intser un operador. Sin embargo, esta opinión está (en su mayoría) en desacuerdo con el estándar.

En primer lugar, [lex.operators/1]enumera los operadores en el idioma. No se deje engañar pensando que estos son simplemente "operadores de preprocesamiento"; no existe tal distinción en el sentido de operadores léxicos . Tampoco tendría sentido, ya que no puede (por ejemplo) ++una macro.

newes, de hecho, una palabra clave (por [lex.key/1]).

A continuación, veamos la nueva expresión en sí. Aquí es donde las cosas se ponen un poco más confusas. Hay, por ejemplo, la siguiente redacción en [expr.new/4]:

En cambio, la versión explícitamente entre paréntesis del nuevo operador se puede utilizar para crear objetos de tipos compuestos.

Considero que esto es un error editorial, ya que está en desacuerdo con las definiciones proporcionadas anteriormente y no ocurre en ningún otro lugar de esa sección.

Luego llegamos a la sobrecarga del operador . En su producción gramatical para una declaración de operador, el terminal que enumera cosas (incluido new) se denomina operador ( [over.oper.general/1]). No creo que debamos preocuparnos por esto. Los nombres de terminales en la gramática nunca han tenido la intención de introducir definiciones de términos. Después de todo, tiene una expresión and que _no necesita ser una operación AND bit a bit; puede ser simplemente una expresión de igualdad :

expresión-y :
   expresión
   - & igualdad y expresión expresión-igualdad

Es común definir gramáticas como esta, y ciertamente no significa que cada expresión de igualdad deba considerarse de alguna manera una invocación del operador AND bit a bit.

Finalmente, algunos han afirmado que la siguiente redacción (también en la sección de sobrecarga del operador) es una prueba de que de newalguna manera ahora, de forma aislada, mágicamente es un operador:

Los operadores new[], delete[], (), y []se forman a partir de más de una ficha

A ellos les digo, no sólo no está newni siquiera en la lista, sino que está claramente usando el término "operador" en el sentido más amplio de "cosas que se pueden sobrecargar", aunque newen sí mismo todavía no es un operador . También está en una nota no normativa, que debería decirle todo lo que necesita saber.

Y, como usted mismo señala, ya lo consideramos operator newotra cosa.

Asteroides con alas
fuente
1
[lex.operators] está claramente incompleto (¡y no pretende ser completo!), ya que [expr.sizeof] normativamente dice que también sizeofes un operador. [expr.throw] es menos explícito con respecto a, throwpero todavía habla de un "operando" throw, lo que implica que throwes un operador.
Konrad Rudolph
7

Si bien sé que está buscando una respuesta semántica, en lo que a opinión respecta, diría que es una "palabra clave" (ya que está claramente reservada), y supongo que ambos sabemos que es simplemente la construcción de C ++ para la asignación de memoria. Dicho esto, cuando pienso en los "operadores" de C ++, tiendo a pensar en funciones con una entrada / salida establecida que se puede anular para tipos específicos. new es el mismo para todos los tipos (y, lo que es más confuso, puede anularse).

No estoy seguro de cuán oficial es cppreference, pero:

La nueva expresión asigna almacenamiento llamando a la función de asignación adecuada. Si el tipo es un tipo que no es de matriz, el nombre de la función es operador nuevo. Si el tipo es un tipo de matriz, el nombre de la función es operador nuevo []

https://en.cppreference.com/w/cpp/language/new

Parece seguirse entonces que new <type>es una expresión para asignar memoria, mientras que newes el operador para esa expresión.

Usando esa misma lógica:

  • sizeofes un "operador" para la expresión sizeof(<type>)(aunque sizeofes un operador de "tiempo de compilación", un poco diferente de lo que estamos acostumbrados)
  • + es un operador para una expresión <type> <operator> <type>
  • throw es un operador para la expresión throw <throwaable>
Pícaro
fuente
Según esa lógica, ¿ []()también es un operador? Es for? Es throw?
Konrad Rudolph
1
@KonradRudolph sí, diría []y ()constituiría operadores separados. Nuevamente, fores absolutamente una palabra clave, pero donde delimitaría ser un operador fue la primera parte de poder ser anulado. Técnicamente, por eso, sizeofno es un operador, lo cual es cierto en C ++ en el sentido de que es más una expresión de preprocesamiento. Y throwes un operador de una expresiónthrow <throwable>
Pícaro
Me refiero específicamente []()a la combinación al definir una lambda. Pero su respuesta también es interesante porque (en C ++) la respuesta depende del contexto: in foo(), ()es un operador (¡sobrecarga!); en (1 + 2)él no es un operador en absoluto (en algunos otros lenguajes, notablemente R, esto es diferente). De todos modos, el criterio anulable no es suficiente. Hay un montón de cosas en C ++ que son los operadores sin ambigüedades pesar de no ser sobrecargable ( ., ::, :?, ambos sizeof s, etc).
Konrad Rudolph
1
Ah, []()como una lambda es una bestia difícil de definir (específicamente porque no estoy tan al día con mi C ++). Así que aquí todavía diría que [] ()constituyen operadores diferentes de una expresión lambda , pero no estoy seguro de si argumentaría que no se pueden anular, o si el mismo acto de implementar / declarar una lambda constituye anular la lambda básica (como en Java). El diseño del lenguaje comienza a convertirse en un arte sobre la ciencia allí. En cuanto a ( ... ), es un separador en la mayoría de los idiomas que he usado, pero echaré un vistazo a R muy rápido
Rogue
2
Como nota al margen, en R todo es una función y todo se puede redefinir. No existe una referencia normativa exhaustiva y no estoy del todo seguro de que los paréntesis se consideren operadores, pero pueden redefinirse; de hecho, la expresión (a + b)es semánticamente idéntica a la llamada de función `(` (a + b), o, de hecho, `(` (`+` (a, b)), donde `x` puede usarse para hacer un nombre xse ajusta sintácticamente a una llamada de función en lugar de especial (como un operador infijo).
Konrad Rudolph