¿Qué modismos de C++ están en desuso en C++ 11?

12 minutos de lectura

avatar de usuario
alan baljeu

Con el nuevo estándar, hay nuevas formas de hacer las cosas, y muchas son mejores que las antiguas, pero la forma antigua sigue estando bien. También está claro que el nuevo estándar no se desaprueba mucho oficialmente, por razones de compatibilidad con versiones anteriores. Entonces la pregunta que queda es:

¿Qué antiguas formas de codificación son definitivamente inferiores a los estilos de C++ 11 y qué podemos hacer ahora en su lugar?

Al responder esto, puede omitir las cosas obvias como “usar variables automáticas”.

  • no puedes desaprobar modismos.

    – pubis

    15 de febrero de 2012 a las 18:33

  • Charla de Herb Sutter en Going Native 2012 cubrió esto:

    – bames53

    15 de febrero de 2012 a las 18:35

  • Ya no se recomienda devolver valores constantes. Obviamente auto_ptr también está en desuso.

    – KerrekSB

    15 de febrero de 2012 a las 18:38


  • Por supuesto que puedes, Pubby. Antes de que se inventaran las plantillas de C++, existía una técnica de macros para crear plantillas. Luego, C ++ los agregó, y la forma anterior se consideró mala.

    – Alan Baljeu

    15 de febrero de 2012 a las 18:41

  • Esta pregunta realmente debe trasladarse a Programmers.se.

    – Nicolás Bolas

    15 de febrero de 2012 a las 19:11

avatar de usuario
Sumante

  1. Clase final: C++11 proporciona la final especificador para evitar la derivación de clases
  2. Las lambdas de C++11 reducen sustancialmente la necesidad de clases de objetos de función con nombre (funtores).
  3. Constructor de movimiento: Las formas mágicas en las que std::auto_ptr las obras ya no son necesarias debido al soporte de primera clase para las referencias de rvalue.
  4. bool seguro: Esto se mencionó anteriormente. Los operadores explícitos de C++11 obvian este modismo muy común de C++03.
  5. Encoger para ajustar: Muchos contenedores STL de C++11 proporcionan una shrink_to_fit() miembro de la función, que debería eliminar la necesidad de intercambiar con un temporal.
  6. Clase Base Temporal: Algunas bibliotecas antiguas de C++ utilizan este lenguaje bastante complejo. Con la semántica de movimiento ya no es necesario.
  7. Escribir enumeración segura Las enumeraciones son muy seguras en C++11.
  8. Prohibir la asignación de montones: Los = delete La sintaxis es una forma mucho más directa de decir que se niega explícitamente una funcionalidad en particular. Esto es aplicable para evitar la asignación de montones (es decir, =delete para miembro operator new), impidiendo copias, cesión, etc.
  9. Definición de tipo con plantilla: Plantillas de alias en C++11 reduce la necesidad de typedefs con plantillas simples. Sin embargo, los generadores de tipos complejos aún necesitan funciones meta.
  10. Algunos cálculos numéricos en tiempo de compilación, como Fibonacci, se pueden reemplazar fácilmente usando expresiones constantes generalizadas
  11. result_of: Usos de la plantilla de clase result_of debe ser reemplazado con decltype. pienso result_of usos decltype cuando esté disponible.
  12. Inicializadores de miembros en clase guarde la escritura para la inicialización predeterminada de miembros no estáticos con valores predeterminados.
  13. En el nuevo código C++ 11 NULL debe redefinirse como nullptrpero mira Charla de STL para saber por qué decidieron no hacerlo.
  14. Plantilla de expresión los fanáticos están encantados de tener la tipo de retorno final sintaxis de función en C++11. ¡No más tipos de devolución largos de 30 líneas!

¡Creo que me detendré allí!

  • ¡Gracias por las cosas detalladas!

    – Alan Baljeu

    22 de febrero de 2012 a las 15:41

  • Gran respuesta, pero atacaría. result_of de la lista. A pesar de lo engorroso typename necesario antes, creo typename result_of<F(Args...)::type a veces puede ser más fácil de leer que decltype(std::declval<F>()(std::declval<Args>()...)y con la aceptación de N3436 en el documento de trabajo ambos trabajan para SFINAE (lo que solía ser una ventaja de decltype que result_of no ofreció)

    –Jonathan Wakely

    18 de enero de 2013 a las 9:17


  • Con respecto a 14) Todavía estoy llorando porque tengo que usar macros para escribir el mismo código dos veces, una para el cuerpo de la función y otra para la instrucción decltype()…

    usuario678269

    31 de marzo de 2014 a las 22:56

  • Me gustaría señalar que este tema está vinculado desde esta página de Microsoft como un artículo “Para obtener más información” en una introducción general al lenguaje C++, ¡pero este tema es altamente especializado! ¿Puedo sugerir que un breve “¡Este tema NO es para principiantes de C++!” ¿Se incluirán consejos al comienzo del tema o esta respuesta?

    – Aacini

    11 de abril de 2015 a las 5:41

  • Re 12: “Inicialización de miembros en clase”: esa es la nueva expresión, no una expresión obsoleta, ¿no? ¿Cambiar el orden de las oraciones tal vez? Re 2: Los funtores son muy útiles cuando desea pasar tipos en lugar de objetos (especialmente en parámetros de plantilla). Entonces es solo alguno usos de funtores que están en desuso.

    – einpoklum

    17 junio 2016 a las 20:59


En un momento dado se argumentó que uno debería regresar por const valor en lugar de solo por valor:

const A foo();
^^^^^

Esto era en su mayoría inofensivo en C++ 98/03, y es posible que incluso haya detectado algunos errores que se parecían a:

foo() = a;

Pero volviendo por const está contraindicado en C++ 11 porque inhibe la semántica de movimiento:

A a = foo();  // foo will copy into a instead of move into it

Así que relájate y codifica:

A foo();  // return by non-const value

  • Sin embargo, ahora se pueden detectar los errores evitables mediante el uso de calificadores de referencia para funciones. Tal como en el caso anterior definiendo A& operator=(A o)& en vez de A& operator=(A o). Estos evitan los errores tontos y hacen que las clases se comporten más como tipos básicos y no evitan la semántica de movimiento.

    – José

    3 de junio de 2013 a las 15:21


Tan pronto como puedas abandonar 0 y NULL a favor de nullptr¡hazlo!

En código no genérico, el uso de 0 o NULL no es gran cosa. Pero tan pronto como comienza a pasar constantes de puntero nulo en código genérico, la situación cambia rápidamente. cuando pasas 0 a un template<class T> func(T) T se deduce como un int y no como una constante de puntero nulo. Y no se puede volver a convertir en una constante de puntero nulo después de eso. Esto cae en cascada en un atolladero de problemas que simplemente no existirían si el universo usara solo nullptr.

C++ 11 no está en desuso 0 y NULL como constantes de puntero nulo. Pero deberías codificar como si lo hiciera.

  • ¿Qué es decltype (nullptr)?

    usuario678269

    31 de marzo de 2014 a las 23:02

  • @GrapschKnutsch: Es std::nullptr_t.

    – Howard Hinant

    01/04/2014 a las 15:25

  • Sugiera que esto se reformule como el idioma en desuso en lugar de la nueva convención a adoptar (por ejemplo, “El uso de 0 o NULL para punteros nulos”).

    – einpoklum

    17 de junio de 2016 a las 21:03

avatar de usuario
kennytm

Idioma bool seguroexplicit operator bool().

Constructores de copias privadas (boost::noncopyable) → X(const X&) = delete

Simulando clase final con destructor privado y herencia virtual → class X final

avatar de usuario
Klaim

Una de las cosas que evita escribir algoritmos básicos en C++ 11 es la disponibilidad de lambdas en combinación con los algoritmos proporcionados por la biblioteca estándar.

Los estoy usando ahora y es increíble la frecuencia con la que solo dices lo que quieres hacer usando count_if(), for_each() u otros algoritmos en lugar de tener que escribir los malditos bucles nuevamente.

Una vez que esté utilizando un compilador de C++ 11 con una biblioteca estándar completa de C++ 11, ya no tienes una buena excusa para no usar algoritmos estándar para construir los tuyos. Lambda acaba de matarlo.

¿Por qué?

En la práctica (después de haber usado esta forma de escribir algoritmos yo mismo) se siente mucho más fácil leer algo que está construido con palabras sencillas que significan lo que se hace que con algunos bucles que tienes que descifrar para saber el significado. Dicho esto, hacer que los argumentos lambda se deduzcan automáticamente ayudaría mucho a hacer que la sintaxis sea más fácil de comparar con un bucle sin formato.

Básicamente, los algoritmos de lectura hechos con algoritmos estándar son mucho más fáciles que las palabras que ocultan los detalles de implementación de los bucles.

Supongo que solo se debe pensar en los algoritmos de nivel superior ahora que tenemos algoritmos de nivel inferior para construir.

  • En realidad hay una buena excusa. estas usando Boost.Rango de algoritmos, que son mucho mejores;)

    – Nicolás Bolas

    15 de febrero de 2012 a las 18:42

  • no veo eso for_each con una lambda es mejor que el bucle for equivalente basado en rango, con el contenido de la lambda en el bucle. El código se ve más o menos igual, pero la lambda introduce una puntuación extra. Puedes usar equivalentes de cosas como boost::irange para aplicarlo a más bucles que solo aquellos que obviamente usan iteradores. Además, el bucle for basado en rango tiene una mayor flexibilidad, ya que puede salir antes si es necesario (mediante return o por break), mientras que con for_each tendrías que tirar.

    –Steve Jessop

    15 de febrero de 2012 a las 18:49


  • @SteveJessop: Aun así, la disponibilidad de rango basado for hace lo habitual it = c.begin(), const end = c.end(); it != end; ++it modismo desaparecido.

    – Ben Voigt

    15 de febrero de 2012 a las 19:03


  • @SteveJessop Una ventaja de la for_each algoritmo sobre el rango basado en bucle es que usted no poder break o return. Es decir, cuando ves for_each sabes inmediatamente sin mirar el cuerpo que no hay tal truco.

    – bames53

    15 de febrero de 2012 a las 19:18


  • @Klaim: para ser específicos, estoy comparando, por ejemplo std::for_each(v.begin(), v.end(), [](int &i) { ++i; }); con for (auto &i : v) { ++i; }. Acepto que la flexibilidad es de doble filo (goto es muy flexible, ese es el problema). No creo que la restricción de no poder usar break en el for_each versión compensa la verbosidad adicional que exige: los usuarios de for_each Aquí están, en mi opinión, sacrificando la legibilidad y la comodidad reales por una especie de noción teórica de que el for_each es en principio más claro y conceptualmente más simple. En la práctica no es más claro ni más sencillo.

    –Steve Jessop

    15 de febrero de 2012 a las 19:36


avatar de usuario
Felipe

Deberá implementar versiones personalizadas de swap con menos frecuencia. En C ++ 03, un no lanzamiento eficiente swap suele ser necesario para evitar copias costosas y tiradas, y dado que std::swap utiliza dos copias, swap a menudo tiene que ser personalizado. En C++, std::swap usos move, por lo que el enfoque cambia en la implementación de constructores de movimiento eficientes y que no lanzan y operadores de asignación de movimiento. Dado que para estos, el valor predeterminado a menudo está bien, será mucho menos trabajo que en C++ 03.

Por lo general, es difícil predecir qué modismos se usarán, ya que se crean a través de la experiencia. Podemos esperar un “C++ 11 efectivo” tal vez el próximo año, y un “C++ 11 Coding Standards” solo en tres años porque aún no se cuenta con la experiencia necesaria.

  • En realidad hay una buena excusa. estas usando Boost.Rango de algoritmos, que son mucho mejores;)

    – Nicolás Bolas

    15 de febrero de 2012 a las 18:42

  • no veo eso for_each con una lambda es mejor que el bucle for equivalente basado en rango, con el contenido de la lambda en el bucle. El código se ve más o menos igual, pero la lambda introduce una puntuación extra. Puedes usar equivalentes de cosas como boost::irange para aplicarlo a más bucles que solo aquellos que obviamente usan iteradores. Además, el bucle for basado en rango tiene una mayor flexibilidad, ya que puede salir antes si es necesario (mediante return o por break), mientras que con for_each tendrías que tirar.

    –Steve Jessop

    15 de febrero de 2012 a las 18:49


  • @SteveJessop: Aun así, la disponibilidad de rango basado for hace lo habitual it = c.begin(), const end = c.end(); it != end; ++it modismo desaparecido.

    – Ben Voigt

    15 de febrero de 2012 a las 19:03


  • @SteveJessop Una ventaja de la for_each algoritmo sobre el rango basado en bucle es que usted no poder break o return. Es decir, cuando ves for_each sabes inmediatamente sin mirar el cuerpo que no hay tal truco.

    – bames53

    15 de febrero de 2012 a las 19:18


  • @Klaim: para ser específicos, estoy comparando, por ejemplo std::for_each(v.begin(), v.end(), [](int &i) { ++i; }); con for (auto &i : v) { ++i; }. Acepto que la flexibilidad es de doble filo (goto es muy flexible, ese es el problema). No creo que la restricción de no poder usar break en el for_each versión compensa la verbosidad adicional que exige: los usuarios de for_each Aquí están, en mi opinión, sacrificando la legibilidad y la comodidad reales por una especie de noción teórica de que el for_each es en principio más claro y conceptualmente más simple. En la práctica no es más claro ni más sencillo.

    –Steve Jessop

    15 de febrero de 2012 a las 19:36


No sé su nombre, pero el código C++03 a menudo usaba la siguiente construcción como reemplazo de la asignación de movimiento faltante:

std::map<Big, Bigger> createBigMap(); // returns by value

void example ()
{
  std::map<Big, Bigger> map;

  // ... some code using map

  createBigMap().swap(map);  // cheap swap
}

Esto evitó cualquier copia debido a la elisión de copia combinada con la swap arriba.

  • En su ejemplo, el intercambio es innecesario, la elisión de copia construiría el valor de retorno en map de todos modos. La técnica que muestras es útil si map ya existe, en lugar de simplemente ser construido. El ejemplo sería mejor sin el comentario “constructor predeterminado barato” y con “// …” entre esa construcción y el intercambio

    –Jonathan Wakely

    31 de enero de 2014 a las 9:57

  • Lo cambié según tu sugerencia. Gracias.

    – Andrzej

    31 de enero de 2014 a las 14:03

  • El uso de “grande” y “más grande” es confuso. ¿Por qué no explicar cómo importan los tamaños de la clave y el tipo de valor?

    – einpoklum

    17/06/2016 a las 21:05

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad