sirve que para array c++ language-lawyer void language-design generic-programming

que - typedef c++



Cuánto se rompería el código C++ existente si void se definiera realmente como `struct void{};` (2)

Hay una propuesta para esto, es p0146: Regular Void.

A continuación se presenta una definición de estructura que es análoga a lo que se propone para el vacío en este documento. La definición real no es un tipo de clase, pero esto sirve como una aproximación bastante precisa de lo que se propone y cómo los desarrolladores pueden pensar en vacío. Lo que se debe tener en cuenta es que se puede considerar que esto agrega una funcionalidad al tipo de vacío existente, de manera muy similar a la de agregar una función miembro especial a cualquier otro tipo existente que no lo tuviera antes, como agregar un constructor de movimientos a un no anteriormente -tipo copiable. Esta comparación no es completamente análoga porque el vacío actualmente no es un tipo ordinario, pero es una descripción razonable e informal, con detalles que se explicarán más adelante.

struct void { void() = default; void(const void&) = default; void& operator =(const void&) = default; template <class T> explicit constexpr void(T&&) noexcept {} }; constexpr bool operator ==(void, void) noexcept { return true; } constexpr bool operator !=(void, void) noexcept { return false; } constexpr bool operator <(void, void) noexcept { return false; } constexpr bool operator <=(void, void) noexcept { return true; } constexpr bool operator >=(void, void) noexcept { return true; } constexpr bool operator >(void, void) noexcept { return false; }

Fue bien recibido en la reunión de Oulu de junio de 2016, Informe de viaje :

Regular void, una propuesta para eliminar la mayoría de los casos de tratamiento de casos especiales de void en el idioma, haciendo que se comporte como cualquier otro tipo. La idea general disfrutó de un mayor nivel de soporte desde su presentación inicial hace dos reuniones, pero algunos detalles aún eran discutibles, sobre todo la posibilidad de eliminar punteros de tipo void *. Se alentó al autor a regresar con una propuesta revisada, y quizás con una implementación para ayudar a descartar complicaciones inesperadas.

Conversé con el autor y él confirmó que básicamente está esperando una implementación, una vez que haya una implementación, planea devolver la propuesta.

Hay una extensa discusión en el documento acerca de qué cambios y por qué, no es realmente citable, pero las preguntas más frecuentes que se abordan son:

  • ¿No presenta esta propuesta más carcasas especiales para el vacío?
  • ¿Por qué no es sizeof (void) igual a 0?
  • ¿Esto rompe std :: enable_if?
  • En la práctica, ¿esto rompería la compatibilidad ABI?
  • ¿No constexpr_if hace que la bifurcación para el vacío sea más fácil?
  • ¿No es ilógico apoyar alguna operación para el vacío?
  • ¿Esto no elimina la noción de "Sin resultado"?
  • ¿No es esto un cambio en el significado de vacío?

void es una verruga rara en el sistema de tipo C ++. Es un tipo incompleto que no se puede completar, y tiene todo tipo de reglas mágicas sobre las formas restringidas en que se puede emplear:

Un tipo cv void es un tipo incompleto que no se puede completar; tal tipo tiene un conjunto de valores vacío. Se utiliza como el tipo de retorno para funciones que no devuelven un valor. Cualquier expresión se puede convertir explícitamente al tipo cv void ([expr.cast]). Una expresión de tipo cv void se usará solo como una declaración de expresión, como un operando de una expresión de coma, como un segundo o tercer operando de ?: ([Expr.cond]), como el operando de typeid , noexcept o decltype , como la expresión en una declaración de return para una función con el tipo de retorno cv void , o como el operando de una conversión explícita al tipo cv void .

(N4778, [basic.fundamental] ¶9 )

Además de la sensación de picazón de todas esas extrañas reglas, debido a las formas limitadas en que se puede usar, a menudo aparece como un caso especial doloroso al escribir plantillas; la mayoría de las veces parece que nos gustaría que se comportara más como std::monostate .

Imaginemos por un momento que, en lugar de la cita anterior, el estándar decía sobre algo void como

Es un tipo con definición equivalente a:

struct void { void()=default; template<typename T> explicit void(T &&) {}; // to allow cast to void };

mientras se mantiene la magia void * : se puede alias cualquier objeto, los punteros de datos deben sobrevivir al viaje de ida y vuelta a través de void * .

Esta:

  • debe cubrir los casos de uso existentes del tipo de void "adecuado";
  • probablemente podría permitir la eliminación de una cantidad decente de basura que se difundió a través de la norma, por ejemplo, [expr.cond] ¶2 probablemente no se necesitaría , y [stmt.return] se simplificaría enormemente (sin dejar de mantener la "excepción" de que return sin expresión está permitido para el void y que "fluir fuera de una función de void es equivalente a return; );
  • debe ser igual de eficiente: la optimización de clase vacía es hoy en día compatible en todas partes;
  • ser intrínsecamente compatible con las ABI modernas, y el compilador aún podría tener un carácter especial en las más antiguas.

Además de ser compatible, esto proporcionaría:

  • construcción, copia y movimiento de esos objetos vacíos, eliminando los casos especiales que generalmente se necesitan en las plantillas;
  • aritmética de puntero adicional en void * , que funciona como para char * , que es una extensión común, bastante útil cuando se manipulan búferes binarios.

Ahora, además de los valores de retorno posiblemente alterados de las cosas de <type_traits> , ¿qué podría romper este código que está bien formado de acuerdo con las reglas actuales (C ++ 17)?


  • void es un tipo con un dominio vacío (no tiene valores posibles);
  • struct foo { } es un tipo con un dominio no vacío (hay un valor de este tipo).

En otras palabras, void es un tipo inferior mientras que una potencial struct void {} sería un tipo de unidad .

Reemplazar el primero con el segundo esencialmente rompe, bueno, el mundo entero. No es del todo distinto de decidir que 0 es igual a 1 en C ++.