Top Qs
Línea de tiempo
Chat
Contexto

C++14

lenguaje de programación De Wikipedia, la enciclopedia libre

Remove ads

C++14 es una versión del estándar del lenguaje de programación C++. La Organización Internacional de Normalización (ISO) aprobó C++14 el 18 de agosto de 2014,[1] y posteriormente lo publicó como la norma ISO/IEC 14882:2014.[2] C++14 reemplazó al anterior estándar C++11 y a su vez fue reemplazado en 2017 por una nueva versión del estándar denominada C++17. El nombre sigue la tradición de denominar a las versiones del lenguaje C++ a partir de la fecha de publicación de la especificación. Durante su fase de desarrollo se utilizó la denominación C++1y para referirse al borrador del estándar, de manera análoga a cómo la denominación C++0x se había utilizado durante el desarrollo de C++11.

Remove ads

Concepción y desarrollo

C++14 es una extensión de C++11 que ofrece principalmente correcciones de errores y algunas mejoras más significativas. El borrador del comité del estándar C++14, N3690, se publicó el 15 de mayo de 2013.[3] El borrador de trabajo N3936, fue publicado el 2 de marzo de 2014, el período de votación definitivo se cerró el 15 de agosto de 2014, y los resultados (aprobación unánime) fueron anunciados el 18 de agosto.[4]

Remove ads

Nuevas características

Resumir
Contexto

Las características que se describen a continuación se basan en el Proyecto de trabajo N3797. Aunque se han realizado algunos cambios menores en la norma final, la mayoría de la información sigue siendo relevante.

Nuevas características del lenguaje

Deducción del tipo de retorno de una función

C++11 introdujo la deducción del tipo de retorno para funciones lambda. C++14 amplía esta capacidad a todas las funciones, incluidas aquellas que no tienen la forma de una simple expresión `return`.[5]

Para deducir el tipo de retorno, se declara la función con `auto` como tipo de retorno, pero sin el especificador de tipo de retorno al final, propio de C++11:

auto DeducirTipoRetorno(); // El tipo de retorno se deduce automáticamente.

Si se utilizan múltiples expresiones `return` en la implementación de la función, todas deben deducir el mismo tipo.[6]

Las funciones que deducen sus tipos de retorno pueden ser declaradas hacia adelante (declaración forward), pero no pueden ser utilizadas hasta que hayan sido definidas. Sus definiciones deben estar disponibles en la unidad de traducción que las utiliza.

La recursión se puede usar con este tipo de funciones, pero la llamada recursiva debe ocurrir después de al menos una instrucción `return` en la función.[6]

auto Correcto(int i) {
  if (i == 1)
    return i;                // El tipo de retorno se deduce como int
  else
    return Correcto(i-1)+i;  // Se llama a sí misma (esto es válido).
}

auto Incorrecto(int i) {
  if (i != 1)
    return Incorrecto(i-1)+i;  // Error: llamada recursiva antes de la deducción del tipo de retorno.
  else
    return i;                  // El tipo de retorno se deduce como int
}

Deducción de tipo con `decltype(auto)`

C++11 añadió dos mecanismos para la deducción de tipos: `auto` y `decltype`. `auto` crea una variable con el tipo apropiado basado en una expresión dada, mientras que `decltype` permite obtener el tipo de una expresión dada. Sin embargo, `auto` y `decltype` deducen los tipos de manera diferente. `auto` siempre deduce un tipo no referenciado (eliminando las referencias, mediante el uso de `std::remove_reference`), mientras que `auto&&` siempre deduce un tipo referenciado. `decltype`, por otro lado, deduce un tipo referenciado o no referenciado dependiendo de la categoría de valor y la naturaleza de la expresión.[5]

int i;
int&& f();
auto x3a = i;             // decltype(x3a) es int
decltype(i) x3d = i;     // decltype(x3d) es int
auto x4a = (i);           // decltype(x4a) es int
decltype((i)) x4d = (i); // decltype(x4d) es int&
auto x5a = f();           // decltype(x5a) es int
decltype(f()) x5d = f(); // decltype(x5d) es int&&

C++14 introduce la sintaxis `decltype(auto)`. Esto permite utilizar las reglas de `decltype` para la deducción de tipo con `auto`.

La sintaxis `decltype(auto)` también se puede usar para la deducción del tipo de retorno de una función, reemplazando `auto` por `decltype(auto)` en la declaración.[6]

Relajación de las restricciones en las funciones `constexpr`

C++11 introdujo el concepto de funciones `constexpr`, que son funciones que pueden ser evaluadas en tiempo de compilación. Estas funciones podían ser usadas en contextos que requieren expresiones constantes, como el argumento de una plantilla entera. Sin embargo, en C++11 las funciones `constexpr` estaban limitadas a contener una única expresión `return` (además de `static_assert` y algunas otras declaraciones).

C++14 relaja estas restricciones. Las funciones `constexpr` ahora pueden contener:[5]

Declaraciones de variables, excepto:
Variables `static` o `thread_local`.
Variables sin inicializadores.
Sentencias condicionales `if` y `switch`.
Bucles, incluyendo bucles `for` basados en rango.
Expresiones que modifican el valor de un objeto, siempre y cuando la vida del objeto haya comenzado dentro de la evaluación de la expresión constante. Esto incluye llamadas a funciones miembro no estáticas que no sean `const`, siempre y cuando cumplan la restricción anterior.

Las instrucciones `goto` están prohibidas en las funciones `constexpr` de C++14.

Además, C++11 especificaba que las funciones miembro no estáticas declaradas como `constexpr` eran implícitamente `const` con respecto a `this`. Esta restricción se ha eliminado; las funciones miembro no estáticas ahora pueden ser no `const`.[7] Sin embargo, una función miembro no `const` `constexpr` solo puede modificar miembros de la clase si la vida de ese objeto comenzó dentro de la evaluación de la expresión constante.

Plantillas de variables

En versiones anteriores de C++, solo las funciones, las clases y los alias de tipo podían ser plantillas. C++14 permite la creación de variables que son plantillas. Un ejemplo de la propuesta original es una variable `pi` que se puede leer para obtener el valor de π con diferentes tipos (por ejemplo, `3` como entero, o el valor más cercano posible con precisión `float`, `double` o `long double` según el tipo).

Se aplican las reglas habituales de las plantillas, incluyendo la especialización.[8][9]

template<typename T>
constexpr T pi = T(3.1415926535897932385);

// Especialización de plantilla:
template<>
constexpr const char* pi<const char*> = "pi";

Inicialización de agregados con inicializadores de miembro

C++11 añadió los inicializadores de miembro, que son expresiones que se aplican a los miembros de una clase si un constructor no los inicializa. La definición de agregados se cambió para excluir explícitamente cualquier clase con inicializadores de miembro, impidiendo el uso de la inicialización de agregados en dichas clases.

C++14 relaja esta restricción,[5] permitiendo la inicialización de agregados en clases con inicializadores de miembro. Si la lista de inicialización entre llaves no proporciona un valor para un miembro, se utilizará el inicializador de miembro correspondiente.[10]

Literales binarios

C++14 permite especificar literales numéricos en binario.[5] Se utiliza el prefijo `0b` o `0B`. Esta sintaxis también se usa en otros lenguajes como Java, Ruby, Python, OCaml, y como una extensión no estándar en algunos compiladores de C al menos desde 2007.[11]

Separadores de dígitos

En C++14, se puede usar el carácter de comilla simple (') como separador de dígitos en literales numéricos, tanto enteros como de coma flotante.[12] Esto facilita la lectura de números grandes mediante la subitización.

auto entero_literal = 1'000'000;
auto punto_flotante_literal = 0.000'015'3;
auto binario_literal = 0b0100'1100'0110;
auto indebido = 1'0'0'000'00; // Agrupación arbitraria, aunque no recomendada

Lambdas genéricas

En C++11, los parámetros de las funciones lambda debían declararse con tipos concretos. C++14 permite declarar los parámetros de una función lambda con el especificador de tipo `auto`.[8]

auto lambda = [](auto x, auto y) {return x + y;};

La deducción de tipo para lambdas genéricas sigue reglas similares a la deducción de argumentos de plantilla (aunque no son idénticas en todos los aspectos). El código anterior es equivalente a:

struct ejemplo_lambda
{
  template<typename T, typename U>
    auto operator()(T x, U y) const {return x + y;}
};
auto lambda = ejemplo_lambda{};

Expresiones de captura en lambdas

Las funciones lambda de C++11 capturan variables del ámbito exterior por copia (valor) o por referencia. Esto impide capturar objetos que son *move-only* (solo se pueden mover, no copiar).[13] C++14 permite inicializar las variables capturadas con expresiones arbitrarias. Esto permite tanto capturar por movimiento como declarar miembros arbitrarios en la lambda, sin necesidad de una variable con el mismo nombre en el ámbito exterior.[8]

Esto se logra utilizando un inicializador:

auto lambda = [valor = 1] {return valor;};

La función `lambda` devolverá `1`, el valor con el que se inicializó `valor`. El tipo de la variable capturada se deduce a partir del inicializador, como con `auto`.

Para capturar por movimiento, se puede usar la función `std::move`:

std::unique_ptr<int> ptr(new int(10));
auto lambda = [valor = std::move(ptr)] {return *valor;};

El atributo `deprecated`

El atributo `deprecated` permite marcar una entidad como obsoleta. Su uso sigue siendo válido, pero se advierte a los usuarios que no se recomienda y puede generar un mensaje de advertencia durante la compilación. Se puede proporcionar una cadena de caracteres literal opcional como argumento de `deprecated` para explicar la razón de la obsolescencia y/o sugerir un reemplazo.

[[deprecated]] int f();

[[deprecated("g() no es seguro para subprocesos, use h() en su lugar")]]
void g( int& x );

void h( int& x );

void prueba() {
  int a = f(); // Advertencia: 'f' está obsoleto.
  g(a); // Advertencia: 'g' está obsoleto: g() no es seguro para subprocesos, use h() en su lugar.
}

Nuevas funciones de la biblioteca estándar

Exclusiones mutuas (mutex) compartidas y bloqueo

C++14 añade un mutex de tipo compartido y un mecanismo de bloqueo compartido asociado.[14][15]

Búsqueda heterogénea en contenedores asociativos

La biblioteca estándar de C++ define cuatro clases de contenedores asociativos (std::set, std::map, std::multiset y std::multimap). Estos contenedores permiten buscar un valor basado en una clave. Sin embargo, la búsqueda siempre se realiza utilizando el tipo de la clave.

C++14 permite realizar la búsqueda utilizando un tipo arbitrario, siempre y cuando exista un operador de comparación que pueda comparar ese tipo con el tipo de la clave.[16] Esto permitiría, por ejemplo, buscar en un `std::map<std::string, int>` usando un `const char*`.

Para mantener la compatibilidad con versiones anteriores, la búsqueda heterogénea solo está permitida si el comparador proporcionado al contenedor asociativo lo habilita. Las clases `std::less<>` y `std::greater<>` se han extendido para permitir operaciones de búsqueda heterogéneas.[17]

Literales definidos por el usuario para tipos de la biblioteca estándar

C++11 definió la sintaxis para sufijos literales definidos por el usuario, pero la biblioteca estándar no utilizaba ninguno. C++14 añade los siguientes literales estándar:[16]

`s`, para crear los diversos tipos `std::basic_string`.
`h`, `min`, `s`, `ms`, `us`, `ns`, para crear los intervalos de tiempo `std::chrono::duration` correspondientes.
auto str = "hola mundo"s; // auto deduce std::string
auto dur = 60s; // auto deduce std::chrono::seconds

Los dos literales `s` no interfieren entre sí, ya que el literal para cadenas solo opera con literales de cadena, y el literal para segundos solo opera con números.[18]

Acceso a tuplas por tipo

El tipo `std::tuple`, introducido en C++11, permite almacenar un conjunto de valores de diferentes tipos, indexados por un entero constante en tiempo de compilación. C++14 extiende esta funcionalidad permitiendo acceder a los elementos de una tupla por tipo, en lugar de por índice.[16] Si la tupla contiene más de un elemento del mismo tipo, se produce un error en tiempo de compilación.[19]

std::tuple<std::string, std::string, int> t("foo", "bar", 7);
int i = std::get<int>(t); // i == 7
int j = std::get<2>(t); // j == 7
std::string s = std::get<std::string>(t); // Error en tiempo de compilación: tipo ambiguo.

Otras funciones de la biblioteca estándar

`std::make_unique` se puede usar para crear objetos `std::unique_ptr` de forma similar a como `std::make_shared` se usa para `std::shared_ptr`.[8]
`std::integral_constant` ahora tiene una sobrecarga de `operator()` que devuelve el valor constante. [16]
Se añaden las funciones globales `std::cbegin` y `std::cend`, que devuelven iteradores constantes al principio y al final de un rango, complementando a `std::begin` y `std::end`.
Remove ads

Véase también

Referencias

Enlaces externos

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads