El autor del material, cuya traducción publicamos hoy, dice que C ++, en su forma moderna, en comparación con lo que era este lenguaje hace varios años, ha cambiado significativamente para mejor. Por supuesto, estos cambios no ocurrieron de inmediato. Por ejemplo, en los viejos tiempos, C ++ carecía de dinamismo. No fue fácil encontrar una persona que pudiera decir que tiene sentimientos tiernos por este idioma. Todo cambió cuando los responsables de estandarizar el lenguaje decidieron dar paso a las innovaciones. En 2011, C ++ se convirtió en un lenguaje dinámico, un lenguaje que evoluciona constantemente y causa a los programadores emociones mucho más positivas.
No pienses que el idioma se ha vuelto más fácil. Todavía puede llamarse uno de los lenguajes de programación más complejos y ampliamente utilizados, si no el más complejo. Pero el C ++ moderno se ha vuelto mucho más amigable que antes.

Hoy hablaremos sobre algunas de las nuevas características del lenguaje (comenzando con C ++ 11, que, por cierto, ya tiene 8 años), que cualquier programador sabrá.
Palabra clave automática
Desde que la palabra clave
auto
apareció en C ++ 11, la vida de los programadores se ha vuelto más fácil. Gracias a esta palabra clave, el compilador puede generar tipos de variables en tiempo de compilación, lo que nos evita tener que especificar siempre los tipos nosotros mismos. Esto resultó ser muy conveniente, por ejemplo, en casos en los que tiene que trabajar con tipos de datos como
map<string,vector<pair<int,int>>>
. Al usar la palabra clave
auto
, hay algunas cosas a tener en cuenta. Considere un ejemplo:
auto an_int = 26;
Preste atención a la última línea en este ejemplo, cuyo comentario está marcado como
#1
(en adelante, de manera similar marcaremos las líneas que analizaremos después de los ejemplos). No hay inicializador en esta línea, no puede hacer esto. El código ubicado en esta línea evita que el compilador sepa cuál debería ser el tipo de la variable correspondiente.
Inicialmente, la palabra clave
auto
en C ++ era bastante limitada. Luego, en las versiones más recientes del idioma, se agregaron funciones
auto
. Aquí hay otro ejemplo:
auto merge(auto a, auto b)
Las líneas n.
#1
y n
#2
aplican la inicialización variable mediante llaves (otra característica nueva en C ++ 11).
Recuerde que cuando usa la palabra clave
auto
, el compilador debe tener alguna forma de inferir el tipo de la variable.
Ahora, una pregunta interesante. ¿Qué sucede si usa un diseño como
auto a = {1, 2, 3}
? Que es esto ¿Vector o causa de error de compilación?
De hecho, una construcción de la forma
std::initializer_list<type>
apareció en C ++ 11. La lista de valores de inicialización entre paréntesis se tratará como un contenedor con la palabra clave
auto
.
Y finalmente, como ya se mencionó, la inferencia de tipos por parte del compilador puede ser extremadamente útil si tiene que trabajar con estructuras de datos complejas. Aquí hay un ejemplo:
void populate(auto &data) {
Echa un vistazo a la línea
#1
. La expresión
auto [v1,v2] = itr.second
representa una nueva característica de C ++ 17. Esta es la llamada descomposición al declarar variables. En versiones anteriores del lenguaje, cada valor tenía que extraerse individualmente. Gracias a este mecanismo, realizar tales operaciones se ha vuelto mucho más conveniente.
Además, si necesita trabajar con datos usando enlaces, es suficiente agregar solo un carácter a esta construcción, convirtiéndolo a la siguiente forma:
auto &[v1,v2] = itr.second
.
Expresiones lambda
C ++ 11 introduce soporte para expresiones lambda. Se asemejan a funciones anónimas en JavaScript, se pueden comparar con objetos funcionales sin nombres. Capturan variables en varios ámbitos según su descripción, para lo cual se utilizan construcciones sintácticas compactas. Además, se pueden asignar a variables.
Las expresiones lambda son una herramienta muy útil para aquellos casos en los que necesita realizar una pequeña operación en el código, pero no desea escribir una función separada para esto. Otro ejemplo común de su uso es la creación de funciones utilizadas en la comparación de valores. Por ejemplo:
std::vector<std::pair<int,int>> data = {{1,3}, {7,6}, {12, 4}};
Puedes encontrar muchas cosas interesantes en este breve ejemplo.
Primero, preste atención a lo conveniente que es usar la inicialización variable usando llaves. A continuación, podemos ver las construcciones estándar
begin()
y
end()
, que también aparecieron en C ++ 11. Luego viene la función lambda, que se utiliza como mecanismo para comparar datos. Los parámetros de esta función se declaran usando la palabra clave
auto
, esta característica apareció en C ++ 14. Anteriormente, esta palabra clave no se podía usar para describir los parámetros de las funciones.
Ahora observe que la expresión lambda comienza con corchetes -
[]
. Esta es la llamada máscara de variables. Determina el alcance de la expresión, es decir, le permite controlar la relación de la expresión lambda con variables y objetos locales.
Aquí hay un extracto de
este repositorio dedicado a las características modernas de C ++:
[]
- la expresión no captura nada. Esto significa que en una expresión lambda es imposible usar variables locales del ámbito externo. Solo se pueden usar parámetros en la expresión.[=]
- la expresión captura los valores de los objetos locales (es decir, variables locales, parámetros). Esto significa que se pueden usar, pero no modificar.[&]
- la expresión captura referencias a objetos locales. Se pueden modificar, como se muestra en el siguiente ejemplo.[this]
: la expresión captura el valor del puntero this
.[a, &b]
: la expresión captura el valor del objeto b
una referencia al objeto b
.
Como resultado, si dentro de la función lambda necesita convertir los datos a otro formato, puede usar los mecanismos anteriores. Considere un ejemplo:
std::vector<int> data = {2, 4, 4, 1, 1, 3, 9}; int factor = 7; for_each(begin(data), end(data), [&factor](int &val) {
Aquí, si el valor accediera a la variable del
[factor]
(entonces la máscara variable
[factor]
se usaría para describir la expresión lambda), entonces en la línea
#1
el valor del
factor
no podría cambiarse, simplemente porque no tendríamos derechos para realizando tal operación. En este ejemplo, tenemos derecho a tales acciones. En tales situaciones, es importante no abusar de las capacidades que las variables de acceso proporcionan por referencia.
Además, tenga en cuenta que
val
también se accede por referencia. Esto asegura que los cambios de datos que ocurren en la función lambda afectan al
vector
.
Expresiones de inicialización variable dentro de construcciones if y switch
Realmente me gustó esta innovación de C ++ 17 justo después de descubrirlo. Considere un ejemplo:
std::set<int> input = {1, 5, 3, 6}; if(auto it = input.find(7); it==input.end()){
Resulta que ahora puede inicializar las variables y compararlas con su uso en un bloque
if
o
switch
. Esto ayuda a escribir código preciso. Aquí hay una descripción esquemática de la estructura en consideración:
if( init-statement(x); condition(x)) {
Realización de cálculos en tiempo de compilación utilizando constexpr
La
constexpr
nos brinda grandes oportunidades. Supongamos que tenemos algún tipo de expresión que necesita ser calculada, mientras que su valor, después de inicializarlo con la variable correspondiente, no cambiará. Tal expresión puede calcularse de antemano y usarse como una macro. O, lo que se hizo posible en C ++ 11, use la
constexpr
.
Los programadores se esfuerzan por minimizar la cantidad de cómputo realizado durante la ejecución del programa. Como resultado, si se pueden realizar ciertas operaciones durante el proceso de compilación y, por lo tanto, eliminar la carga del sistema durante la ejecución del programa, esto tendrá un buen efecto en el comportamiento del programa durante la ejecución. Aquí hay un ejemplo:
#include <iostream> constexpr long long fact(long long n) { // constexpr return n == 1 ? 1 : (fact(n-1) * n); } int main() { const long long bigval = fact(20); std::cout<<bigval<<std::endl; }
Este es un ejemplo muy común del uso de
constexpr
.
Como
constexpr
la función para calcular el factorial como
constexpr
, el compilador puede calcular previamente el valor
fact(20)
en el momento de la compilación del programa. Como resultado, después de la compilación, la cadena
const long long bigval = fact(20);
puede ser reemplazado por
const long long bigval = 2432902008176640000;
.
Tenga en cuenta que el argumento pasado a la función está representado por una constante. Esta es una característica importante de usar funciones declaradas usando la
constexpr
. Los argumentos que se les pasan también deben declararse con la
constexpr
o con la palabra clave
const
. De lo contrario, dichas funciones se comportarán como funciones ordinarias, es decir, durante la compilación, sus valores no se calcularán de antemano.
Las variables también se pueden declarar utilizando la
constexpr
. En este caso, como puede suponer, los valores de estas variables deben calcularse en tiempo de compilación. Si esto no se puede hacer, se mostrará un mensaje de error de compilación.
Es interesante observar que más tarde, en C ++ 17,
aparecieron las
construcciones constexpr-if y
constexpr-lambda .
Estructuras de datos de tupla
Al igual que la estructura de datos de
pair
, la estructura de datos de
tuple
(tupla) es una colección de valores de diferentes tipos de un tamaño fijo. Aquí hay un ejemplo:
auto user_info = std::make_tuple("M", "Chowdhury", 25);
A veces, en lugar de una estructura de datos de
tuple
, es más conveniente usar
std::array
. Esta estructura de datos es similar a las matrices simples utilizadas en el lenguaje C, equipadas con características adicionales de la biblioteca estándar de C ++. Esta estructura de datos apareció en C ++ 11.
Inferir automáticamente el tipo de argumento de plantilla de clase
El nombre de esta característica parece bastante largo y complejo, pero de hecho no hay nada complicado aquí. La idea principal aquí es que en C ++ 17, la salida de tipos de argumentos de plantilla también se realiza para plantillas de clase estándar. Anteriormente, esto solo era compatible con plantillas funcionales. Como resultado, resulta que solían escribir así:
std::pair<std::string, int> user = {"M", 25};
Con el lanzamiento de C ++ 17, esta construcción ahora se puede reemplazar con esto:
std::pair user = {"M", 25};
La inferencia de tipos se realiza implícitamente. Este mecanismo es aún más conveniente de usar cuando se trata de tuplas. Es decir, antes tenía que escribir lo siguiente:
std::tuple<std::string, std::string, int> user ("M", "Chy", 25);
Ahora lo mismo se ve así:
std::tuple user2("M", "Chy", 25);
Vale la pena señalar que estas características no parecerán algo digno de atención para aquellos que no están particularmente familiarizados con las plantillas de C ++.
Punteros inteligentes
Trabajar con punteros en C ++ puede ser una verdadera pesadilla. Gracias a la libertad que el lenguaje le da al programador, a veces es muy difícil para él, como dicen, "no dispararse en el pie". En muchos casos, los punteros están presionando para que ese "disparo" del programador.
Afortunadamente, C ++ 11 introdujo punteros inteligentes que son mucho más convenientes que los punteros regulares. Ayudan al programador a evitar pérdidas de memoria al liberar recursos cuando sea posible. Además, proporcionan una garantía de seguridad para excepciones.
Resumen
Aquí hay un buen repositorio, que, creemos, será interesante para aquellos que siguen las innovaciones de C ++. Algo nuevo aparece constantemente en este idioma. Aquí tocamos solo algunas características modernas del lenguaje. De hecho, hay muchos de ellos. Es posible que aún hablemos de ellos.
Estimados lectores! ¿Qué características modernas de C ++ encuentra más interesantes y útiles?
