c++ c++11 pointers language-lawyer comparison-operators

¿Por qué C++ 11 contiene una cláusula impar sobre la comparación de punteros nulos?



c++11 pointers (1)

TL; DR:

  • en C ++ 98/03 la cláusula no estaba presente, y la norma no especificaba los operadores relacionales para los punteros de void (problema central 879, ver el final de esta publicación);
  • la cláusula impar sobre la comparación de los punteros void se agregó en C ++ 11 para resolverla, pero esto a su vez dio origen a otros dos problemas centrales 583 y 1512 (ver más abajo);
  • la resolución de estos problemas requirió que la cláusula fuera eliminada y reemplazada con la redacción encontrada en el estándar C ++ 14, que permite la comparación "normal" de void * .

Problema central 583: comparaciones de punteros relacionales con la constante de puntero nula

  1. Comparaciones de punteros relacionales con la constante de puntero nula Sección: 8.9 [expr.rel]

En C, esto está mal formado (ver C99 6.5.8):

void f(char* s) { if (s < 0) { } } ...but in C++, it''s not. Why? Who would ever need to write (s > 0) when they could just as well write (s != 0)?

Esto ha estado en el idioma desde el ARM (y posiblemente antes); aparentemente es porque las conversiones de puntero (7.11 [conv.ptr]) se deben realizar en ambos operandos siempre que uno de los operandos sea de tipo puntero. Así que parece que la conversión "null-ptr-to-real-pointer-type" está enganchando a un paseo con las otras conversiones de puntero.

Resolución propuesta (abril, 2013):

Este problema se resuelve mediante la resolución del problema 1512 .

Core Issue 1512: comparación de puntero vs conversiones de calificación

  1. Comparación de punteros frente a conversiones de calificación Sección: 8.9 [expr.rel]

De acuerdo con 8.9 [expr.rel] párrafo 2, que describe las comparaciones de punteros,

Las conversiones de puntero (7.11 [conv.ptr]) y las conversiones de calificación (7.5 [conv.qual]) se realizan en operandos de puntero (o en un operando de puntero y una constante de puntero nula, o en dos constantes de puntero nulas, al menos una de ellas no es integral) para llevarlos a su tipo de puntero compuesto. Esto parece hacer que el siguiente ejemplo esté mal formado,

bool foo(int** x, const int** y) { return x < y; // valid ? } because int** cannot be converted to const int**, according to the rules of 7.5 [conv.qual] paragraph 4.

Esto parece demasiado estricto para la comparación de punteros, y las implementaciones actuales aceptan el ejemplo.

Resolución propuesta (noviembre de 2012):

En el artículo se encuentran extractos relevantes de la resolución de los problemas anteriores : comparación de puntero frente a conversiones de calificación (revisión 3) .

Lo siguiente también resuelve el problema central 583 .

Cambio en 5.9 expr.rel párrafos 1 a 5:

En esta sección, la siguiente declaración (la cláusula impar en C ++ 11 ) se ha eliminado :

Los punteros a void (después de las conversiones de puntero) se pueden comparar, con un resultado definido de la siguiente manera: Si ambos punteros representan la misma dirección o son ambos valores de puntero nulo, el resultado es true si el operador es <= o >= y false contrario ; de lo contrario el resultado no se especifica

Y se han añadido las siguientes afirmaciones:

  • Si dos punteros apuntan a elementos diferentes de la misma matriz, o a subobjetos de la misma, el puntero al elemento con el subíndice más alto se compara con mayor.
  • Si un puntero apunta a un elemento de una matriz, o un subobjeto de la misma, y ​​otro puntero apunta uno más allá del último elemento de la matriz, el último puntero se compara en mayor.

Por lo tanto, en el borrador de trabajo final de la sección [expr.rel] / 3 de C ++ 14 (n4140), se encuentran las declaraciones anteriores tal como se indicaron en el momento de la resolución.

Excavar por la razón por la cual se agregó esta cláusula impar me llevó a un problema mucho anterior 879: Faltan operadores de comparación integrados para los tipos de punteros . La resolución propuesta de este problema (en julio de 2009) llevó a la adición de esta cláusula que se votó en WP en octubre de 2009.

Y así fue como se incluyó en el estándar C ++ 11.

Mientras revisaba las referencias para otra pregunta, noté una cláusula impar en C ++ 11, en [expr.rel] ¶3:

Los punteros a void (después de las conversiones de puntero) se pueden comparar, con un resultado definido de la siguiente manera: Si ambos punteros representan la misma dirección o son ambos valores de puntero nulo, el resultado es true si el operador es <= o >= y false contrario ; De lo contrario, el resultado no se especifica.

Esto parece significar que, una vez que se han vaciado dos punteros para void * , su relación de orden ya no está garantizada; por ejemplo, esto:

int foo[] = {1, 2, 3, 4, 5}; void *a = &foo[0]; void *b = &foo[1]; std::cout<<(a < b);

Parecería estar sin especificar.

Curiosamente, esta cláusula no estaba en C ++ 03 y desapareció en C ++ 14, por lo que si tomamos el ejemplo anterior y le aplicamos la redacción de C ++ 14, diría que ¶3.1

  • Si dos punteros apuntan a elementos diferentes de la misma matriz, o a subobjetos de la misma, el puntero al elemento con el subíndice más alto se compara con mayor.

se aplicaría, como a y b apuntan a elementos de la misma matriz, aunque se hayan vaciado para void * . Tenga en cuenta que la redacción de ¶3.1 era prácticamente la misma en C ++ 11, pero parecía estar anulada por la cláusula void * .

¿Estoy en lo cierto en mi entendimiento? ¿Cuál fue el punto de esa cláusula extraña agregada en C ++ 11 e inmediatamente eliminada? ¿O tal vez todavía está allí, pero se trasladó a / implícito en alguna otra parte del estándar?