P0137 presenta la plantilla de función std::launder y hace muchos, muchos cambios a la norma en las secciones sobre los sindicatos, toda la vida, y los punteros.

¿Cuál es el problema de este trabajo es resolver? ¿Cuáles son los cambios en el lenguaje, que debo tener en cuenta? Y lo que vamos a laundering?

  • Estás preguntando sobre el propio documento o sobre el std::launder? std::launder se utiliza para «obtener un puntero a un objeto creado en el almacenamiento ocupado por un objeto existente del mismo tipo, incluso si se ha const o de referencia de los miembros».
  • útil enlace sobre el tema. También esta pregunta stackoverflow.com/questions/27003727/…
  • Esto ha sido puesto en libertad en VC2017 en la versión 15.7.0
  • De acuerdo a las ets, los punteros son triviales tipos, para el blanqueo de no hacer nada. 😉
InformationsquelleAutor Barry | 2016-09-08

1 Comentario

  1. 228

    std::launder es acertadamente llamado, aunque sólo si usted sabe qué es. Se realiza memoria de lavado de.

    Considere el ejemplo en el papel:

    struct X { const int n; };
    union U { X x; float f; };
    ...
    
    U u = {{ 1 }};

    La declaración que realiza el agregado de inicialización, inicializando el primer miembro de U con {1}.

    Porque n es un const variable, el compilador es libre de asumir que u.x.n se siempre 1.

    Entonces, ¿qué sucede si hacemos esto:

    X *p = new (&u.x) X {2};

    Porque X es trivial, no tenemos necesidad de destruir el viejo objeto antes de crear uno nuevo en su lugar, así que esto es perfectamente legal código. El nuevo objeto tendrá sus n miembro 2.

    Así que dime… ¿qué u.x.n volver?

    La respuesta obvia será de 2. Pero eso está mal, porque el compilador se puede asumir que una verdadera const variable (y no meramente un const&, pero una variable de objeto declarado const) nunca va a cambiar. Pero sólo hemos cambiado.

    [basic.la vida]/8 detalla las circunstancias en las que sea OK para acceder al objeto recién creado a través de variables/punteros o referencias a la antigua. Y tener un const miembro de la descalificación de los factores.

    Así que… ¿cómo podemos hablar de u.x.n correctamente?

    Tenemos que lavar nuestra memoria:

    assert(*std::launder(&u.x.n) == 2); //Will be true.

    De lavado de dinero se utiliza para evitar que la gente de seguimiento de dónde sacó su dinero de. Memoria de lavado se utiliza para prevenir el compilador de seguimiento de dónde sacó su objeto, por lo que obliga a evitar cualquier optimizaciones que ya no se aplican.

    Otro de la descalificación de los factores es que si se cambia el tipo del objeto. std::launder puede servir también de ayuda:

    aligned_storage<sizeof(int), alignof(int)>::type data;
    new(&data) int;
    int *p = std::launder(reinterpret_cast<int*>(&data));

    [basic.la vida]/8 nos dice que, si asigna un nuevo objeto en el almacenamiento de la antigua, usted no puede tener acceso al nuevo objeto a través de los punteros a la antigua. launder nos permite el paso al costado que.

    • Así es mi tl;dr correcta: «lavado de dinero es básicamente para los no-UB tipo descriptivo»?
    • Pero tenía hacer un reinterpret_cast. ¿Por qué no es algo como std::launder<int *>(&data) (en donde el parámetro formal es un puntero void)?
    • Porque no siempre se necesita el elenco. El propósito de blanquear es la de no realizar un tipo de fundición. Es para hacer el tipo de fundición (y varias otras formas de inteligencia que no implican conversiones legal. Si usted acaba de hacer el tipo de fundición por sí mismo, tendría UB por el estricto aliasing regla.
    • Hay una razón por la que necesita la unión en su ejemplo? ¿No sería el mismo ser cierto ya para el común de los structs?
    • Podría usted explicar por qué esto es cierto? «Porque n es un const variable, el compilador es libre de asumir que u.x.n siempre será 1.» Donde en el estándar hace decir eso? Lo pregunto porque el gran problema que señaló implicaría para mí que es falso, en primer lugar. Sólo debe ser verdadera, bajo el como-si la regla, lo que falla aquí. Lo que me estoy perdiendo?
    • «Pero eso está mal, porque el compilador se puede asumir que una verdadera variable const … nunca va a cambiar. Pero sólo hemos cambiado.» Por qué no puede el compilador de la figura que por su propia cuenta (al menos en este ejemplo)?
    • se crea un nuevo objeto en la ubicación de almacenamiento que el objeto original ocupados […] el nombre del objeto original, automáticamente se refieren al nuevo objeto […] si: […] el tipo de […] no contiene ningún no-estático miembro de datos cuyo tipo es const-calificado o un tipo de referencia […]«
    • &u.x es un puntero a int const. ¿Por qué se permitió el uso de un puntero a datos const en una colocación de nuevo?
    • Necesitamos que el «else» cláusula de que el segundo «si» aquí, sin embargo, no? Como, por ejemplo, lo que si hay un miembro de datos, pero nunca fue consultado? Sería su existencia realmente importa?
    • Es un puntero a un X, no const int. Y usted necesita para permitir esta opción si desea que la colocación de nuevo a trabajar en las estructuras const miembros, que generalmente se desea. Incluso const int tipo de sentido. Pensar asignadores personalizados para grandes cantidades de pequeños, del mismo tamaño de los objetos que colocarlos todos en un bloque de memoria asignado para ese fin.
    • por ejemplo, ¿si existiera un miembro de datos, pero nunca fue consultado?» es Irrelevante. [basic.la vida]/8 hechizos cuando usted necesita para lavar su memoria. El hecho de que un compilador podría, en una estructura en particular, no se requiere no importa. El estándar lo requiere.
    • ¿Cuánto podemos soslayar que aliasing regla? Como template <class T, class U> T* alias_cast(U* ptr) { return std::launder(reinterpret_cast<T*>(ptr)); } Cómo UB es eso?
    • Muy; si no hay objetos de tipo T se encuentra en la dirección ptr representa, luego de romper launder‘s condición previa, así que no hay punto de hablar acerca de los resultados.
    • Bien, no. Es para ‘descriptivo’ de los nombres o de los punteros, de tal forma que legalmente puede ser utilizado para acceder a las distintas instancias de objeto/vidas de sus originales referentes. No es de tipo descriptivo, que se mantiene como la UB como siempre.
    • No estoy seguro de donde me dijo nada sobre el particular, compiladores o particular de las estructuras. Yo estaba hablando sólo de la uso, y me estaba diciendo que la oración necesita una «cosa» (cláusula o necesitamos más contexto) para abordar esta situación.
    • No necesitamos una cláusula else allí. La situación que usted desea tener en cuenta es abordado como es. La norma deja muy claro que no importa si el const-miembro calificado es cada vez que se accede o no. El hecho de que el miembro que existe es suficiente para exigir launder en todo tipo contenedor.
    • Cuál es la forma correcta de hacer en lugar de tipo descriptivo, si no por blanqueo de los punteros
    • C++ no permite hacer «en lugar de tipo descriptivo». No en un sentido general. Lo más cerca que se puede obtener es de la nueva redacción de los sindicatos, pero que no le permite acceder al valor a través de un objeto de un tipo diferente. C++ soporta solamente de tipo descriptivo por copia.
    • Puede usted pensar en ninguna razón por qué las personas que supuestamente están interesados en «optimización» no serían capaces de proporcionar un medio por el cual los programadores pueden utilizar de forma segura las técnicas que proporcionan un 2x o mejor impulso de velocidad (y en algunos casos, podría permitir órdenes de magnitud de velocidad de impulso al tratar con ciertas Api). Entiendo la necesidad que tienen los compiladores de hacer la optimización de los supuestos en los casos donde no tienen ninguna razón para creer que estás inseguro, pero que requieren de los programadores escribir código ineficiente no parece una receta para un buen rendimiento.
    • se te ocurre alguna razón…» estás quejando a la persona equivocada. Si no te gusta cómo C++ se define en este sentido, la tome con el comité de normas. Sólo estoy explicando cómo la norma dice que las cosas funcionen.
    • Uno sólo puede esperar supercat hace mucho cabildeo de los Comités como lo hacen infinitamente exigiendo respuestas de compañeros de terceros usuarios del lenguaje en TAN. Además, una buena optimización del compilador de optimizar su correcta solución de memcpy en una reinterpretación sobre el soporte (es decir, lax alineación) de las plataformas de de todos modos.
    • Para confirmar que puedo entender esto correctamente, si se reescribió que la última sección como int* totallyNewPtr = new (&data) int;, no habría necesidad de lavar nada porque totallyNewPtr es un nuevo puntero al objeto que no sea necesario acceder a través de cualquier caducado objetos?
    • Tanto tiempo que no uso el otro puntero, sí.
    • Me pregunto si las futuras implementaciones chase este uso y prueba para descartar en tiempo de compilación, ya que en la actualidad funciona 🙂
    • Buena pregunta, que realiza el enlace. Lástima que muchos de la gente de aquí el culto de la lengua normas tanto como a sus dioses que se olvidan de que en algún momento, las normas tienen que ser implementados en máquinas reales que pueden requerir haciendo cosas que no están definidos por la letra de la norma. Eliminar los punteros null puede ser sin duda entre estas cosas. Me hubiera upvoted tanto su pregunta y su respuesta, de no haber sido eliminado ya.
    • He guardado aquí. La pregunta que tengo cerrado en menos de 15 minutos, y se elimina en menos de 24 horas. Meta no importa demasiado. Y, de hecho, me espera y lo hizo a propósito. Como salvaguardar he preparado tanto de preguntas y respuestas para publicar en el mismo tiempo antes de que pudiera llegar cerrado. Esto sucedió y yo era consciente de que ya había gente tratando de responder y no podía más. He dejado de publicar preguntas/respuestas después de esto. Yo sólo quería ver cómo hostil que tiene. Incluso hubo alguien que pregunta si sé latina para el uso de un determinado plazo.
    • Creo que he encontrado otro caso de uso para std::lave pero me pongo no answer. Usted podría estar interesado en responder a ella.
    • El punto más importante que la gente olvida es que uno de los objetivos del Comité de clasificación de varias cosas como la UB fue «para permitir una cierta variedad entre las implementaciones que permite que la calidad de la implementación a ser una fuerza activa en el mercado, así como para permitir que ciertas extensiones populares, sin quitar los cachet de conformidad a la Norma.» El hecho de que un conforme la aplicación se pueden saltar sobre los rieles en un determinado caso no implica ningún juicio de que dicho comportamiento sería adecuado en la calidad de las implementaciones.
    • Hay casos conocidos en los que realmente se requieren en el conocimiento de los compiladores? El ejemplo de arriba no muestran una necesidad de lavar en clang con –std=c++11 o-std=c++17 con -O3.
    • ¿Importa? Si todo lo que importa es si los compiladores del «trabajo» de hoy con alguna pieza de código, se puede encontrar por solo la comprobación de las mismas. Pero sin quitar la UB a partir de su código, usted no tiene ninguna efectiva la garantía de que un compilador no se de repente decide hacer su código no funciona. O de que podría trabajar en un caso y no en otro. O lo que sea.
    • Es importante que, a pesar de no quiero UB en mi código, es muy satisfactorio ver una característica del lenguaje como esta realmente resolver un problema. Yo estoy a favor de lenguaje pedante y de problemas hipotéticos problemas, pero viendo el comportamiento de obtener fija es mucho más satisfactorio.
    • Yo no obtener esta respuesta en absoluto. launder es una función de la librería. Qué hace realmente?
    • Permite acceder al objeto detrás de un puntero cuando la norma de lo contrario, dicen que no se puede. Como en el caso descrito anteriormente. Después de la colocación de nuevo, u.x.n no puede ser utilizada para acceder al objeto recién creado, porque de la norma citada. El uso de launder le permite tener acceso a ese objeto.
    • No veo cómo una función de la biblioteca, en contraposición a una característica del lenguaje, puede hacer eso.
    • Se puede hacer eso porque el estándar de dice que. No hay distinción entre el lenguaje y la biblioteca; todos ellos son una cosa, y todos ellos han de-jure autoridad sobre C++. Así que si la norma dice que launder proporciona un puntero al objeto directo en que la memoria, a continuación, se proporciona un puntero al objeto directo en la memoria.
    • Pero de la biblioteca estándar de funciones se implementan como el código de C++, ¿no? Es esta una excepción?
    • Ese es un asunto de lo «aplicado» significa. El comportamiento de launder con respecto a la generación de código es que te pongas bien definido de comportamiento en determinadas circunstancias. Ese comportamiento es generado por el compilador; se ve que ha realizado la memoria de lavado de dinero y que por tanto, no puede asumir las cosas de otra manera sería capaz de asumir. Es decir, cuando el optimizador está pasando y convertir su u.x.n utiliza en el literal 1, golpea launder y se va «Uy, no puede ver a través», y se detiene.
    • ¿No es esto sólo un problema de que el compilador no reconocer que la colocación de nuevos modifica el objeto? Mientras n es const, x no es const. La colocación de nuevas modifica el objeto u.x, por lo tanto, el compilador no debe asumir que cualquier parte de u.x tiene el mismo valor que tenía antes. No hay blanqueo de capitales debe ser necesaria.
    • Pero la colocación new no modificar el objeto; finaliza el objeto viejo y crea uno nuevo en su lugar. Las Variables en C++ nombre de los objetos; si la colocación-new sobre un objeto, entonces la variable no nombre más. C++ permite que el nombre de la antigua objeto para referirse a la nueva bajo ciertas circunstancias. Pero fuera de esas circunstancias, el nombre se refiere a la edad de objeto. Lo que queremos es un sistema donde los nombres no el nombre de los objetos, y por lo tanto, cualquier const-declaró nombre no podía ser asumido para referirse a nombre de la misma const objeto. Así que ¿qué sería de const realmente?
    • Independientemente de si es el mismo objeto o no, el compilador puede ver la colocación de nuevo y saber que cualquiera que sea la etiqueta u.x se refiere a que se ha modificado o reemplazado. Por lo tanto nada de lo que se refiere a este después no debe asumir que no ha sido cambiado. De nuevo, no hay lavado de dinero necesaria. Pidiendo el programador para poner este tipo de cosas en forma explícita que es tonto.
    • Excepto cuando no lo ve, porque está detrás de una pre-compilado de la biblioteca. O algo cargado desde un archivo DLL. O por cualquier otra razón que el compilador elegiría no a en línea a través de una llamada a una función. O… si la colocación denew está dentro de un condicional declaración, por lo que el compilador no podemos saber en tiempo de compilación de la rama que pasó. Pidiendo un compilador para saber acerca de las cosas que no podemos saber acerca de que es tonto.
    • Esos son todos los casos que el compilador no puede optimizar de todos modos.
    • No puede optimizar qué? Si usted llama a un opaco función entre la creación de u y el acceso a ella, incluso si se pasa una referencia a u como un parámetro, el compilador, que sin duda puede optimizar u.x.n. Por qué? Porque const objetos no ser alterado, y por lo tanto, cualquier código que lo hizo ha provocado la UB. Con el fin de regresar el valor original es válida «indefinido» de la conducta. Lo que estamos pidiendo es simplemente para que no indefinido en virtud de algunos mal circunstancias específicas. ¿Por qué molestarse? Por qué no sólo respecto de que el nombre de un const objeto siempre se refiere a los originales const objeto?
    • Si pasa u o u.x como una variable puntero o una referencia a un opaco función, el compilador absolutamente tiene que asumir que u.x.n puede haber sido cambiado por la función. Puede nunca supongamos lo contrario. Si pasa u o u.x como una constante de puntero o referencia, entonces sí, se puede asumir que no ha cambiado, pero cualquier bien definida programa no se puede cambiar de todos modos. Por lo tanto todavía hay ningún requisito explícito de lavado de anotaciones.
    • Permítanos continuar esta discusión en el chat.
    • Llegando muy tarde, pero ¿por qué necesitamos la unión de motivar ejemplo? No una simple colocación de nuevo en un único objeto de ser exactamente la misma cosa?
    • El ejemplo deja claro que el problema se manifiesta basa tanto en la forma de sacar el puntero de referencia, así como la naturaleza de la tipo. Sin const, el código estaría bien, pero con el mismo, usted necesita launder para obtener un puntero al objeto. Mientras que, con sólo algunos sin inicializar de almacenamiento, que siempre había necesidad de launder para obtener el puntero de la dirección de la de almacenamiento.

Dejar respuesta

Please enter your comment!
Please enter your name here