C++14

De Wikipedia, la enciclopedia libre

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, análogamente a cómo la denominación C++0x se había utilizado durante el desarrollo de C++11.

Concepción y desarrollo[editar]

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

Nuevas características[editar]

Las características descritas a continuación son las que aparecen en el Proyecto de trabajo N3797. Todavía no han sido actualizadas para los cambios finales (relativamente menores) en la norma.

Nuevas características del lenguaje[editar]

Deducción del tipo retorno de una función[editar]

C++11 permite deducir el tipo de retorno para funciones lambda, C++14 proporciona esta capacidad en todas las funciones. También se extiende para funciones que no son de la forma de expresión return.[5]

Con el fin de deducir el tipo de retorno, la función debe ser declarada con auto como el tipo cambiado, pero sin el posterior retorno especificador de tipo del C++11:

auto DeducirTipoRetorno(); // Tipo de retorno a deducir.

Si múltiples expresiones de retorno se utilizan en la implementación de la función, todos ellos deben deducir del mismo tipo.[6]

Funciones que deducen sus tipos de retorno pueden ser declaradas hacia adelante, pero no pueden ser utilizados hasta que hayan sido definidos. Sus definiciones deben estar disponibles para la unidad de traducción que los utiliza.

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

auto Correcto(int i) {
  if (i == 1)
    return i;                //Tipo de retorno deducido como int
  else
    return Correcto(i-1)+i;  //Se llama a sí mismo.
}

auto Incorrecto(int i) {
  if (i != 1)
    return Incorrecto(i-1)+i;  //Demasiado pronto para llamar a esto. Ninguna declaración de retorno antes.
  else
    return i;                  //Tipo de retorno deducido como int
}

Deducción alternativa[editar]

En C++11, son agregados dos métodos de deducción de tipo. auto es una manera de crear una variable del tipo apropiado, basado en una expresión dada, y decltype es para computar el tipo de una expresión dada. Sin embargo, el camino decltype y auto deducen tipos diferentes. Especialmente, auto siempre deduce un tipo no por referencia, mediante el uso de std::remove_reference, mientras auto&& siempre deduce un tipo sobre referencia. Sin embargo, decltype puede ser forzado, sin importar si es deducido del tipo sobre la referencia, basado en la categoría de valor de la expresión y la naturaleza de la expresión deducida.[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 agregará la sintaxis decltype(auto). Esto permite auto declaraciones a usar las reglas de decltype sobre la expresión.

La sintaxis decltype(auto) también puede usarse mediante el uso de la sintaxis decltype(auto) en lugar de auto para la deducción del tipo de retorno de la function.[6]

Extensiones de constexpr[editar]

C++11 introdujo el concepto de constexpr-declared, una función que podría ser ejecutada en tiempo de compilación. Sus valores de retorno podrían ser consumidos por las operaciones que requieren expresiones constantes, como un argumento de plantilla entera, sin embargo, las funciones C++11 constexpr solo podían contener una sola expresión que se devuelve (como static_assert y un pequeño número de otras declaraciones).

En C++14 las funciones Constexpr-declared ahora pueden tener y realizar:[5]

  • Todas las declaraciones excepto:
    • Variables static o thread_local.
    • Declaraciones de variables sin inicializadores.
  • Las declaraciones de ramificación condicional if y switch.
  • Todas las declaraciones de bucle, incluyendo rango basados en for.
  • Expresiones que cambian el valor de un objeto si su tiempo de vida comenzó dentro de la expresión constante de una función. Esto incluye llamadas a cualquiera que no sea declaraciones const constexpr, miembros de funciones no estáticas.

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

También, C++11 sostiene que todos los miembros de las funciones no estáticas que son declaradas constexpr son también implícitamente declaradas const, con respecto a this. Eso ya se eliminó, los miembros de las funciones no estáticas pueden ser no const.[7]​ Sin embargo, por las restricciones anteriores, los miembros de las funciones no const constexpr solo puede modificar un miembro de la clase si esa vida de objeto comenzó dentro de la evaluación de la expresión constante.

Plantillas variables[editar]

En versiones anteriores de C++, solo funciones, clases o alias de tipo podían ser plantillas. C++14 ahora permite la creación de variables que son plantillas. Un ejemplo dado en la propuesta es una variable pi que se puede leer para obtener el valor de pi para varios tipos (ejemplo, 3 cuando se lee como un tipo integral, el valor más cercano posible con precisión float, double o long double cuando se lee como float, double o long double, respectivamente).

Las reglas habituales de las plantillas se aplican a este tipo de declaraciones y definiciones, incluyendo la especialización.[8][9]

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

// Reglas de especialización habituales aplican:
template<>
constexpr const char* pi<const char*> = "pi";

Agregado de la inicialización del miembro[editar]

C++11 agregó los inicializadores del miembro, expresiones que se aplicará a los miembros en el ámbito de clase si un constructor no se ha inicializado el miembro. La definición de los agregados se cambió para excluir explícitamente cualquier clase con la inicialización del miembro, entonces no se les permite utilizar la inicialización agregada.

C++14 relajará esta restricción,[5]​ permitiendo inicialización agregada sobre dichos tipos. Si la lista de inicialización arriostrados no proporciona un valor para este argumento, el inicializador miembro se hará cargo de ella.[10]

Literales binarios[editar]

Los literales numéricos en C++14 se pueden especificar en el binario.[5]​ La sintaxis utiliza los prefijos 0b o 0B. La sintaxis se utiliza también en otros lenguajes de programación, como Java, Ruby, Python, OCaml, y como una extensión no oficial en algunos compiladores de C por lo menos desde 2007.[11]

Agrupación de dígitos[editar]

En C++14, el carácter comilla simple, también puede usarse como separador de millares en los literales numéricos, ambos entero literal (programación) y puntos flotantes literales.[12]​ Esto puede hacer que sea más fácil para los lectores humanos para analizar grandes cantidades a través de la simultaneidad (matemática).

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;

Lambda genérico[editar]

En C++11, en una función lambda los parámetros tienen que ser declarados con tipos concretos. C++14 relaja este requerimiento, permitiendo que los parámetros de función lambda que se declaran con la especificación de tipo auto.[8]

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

Como para la deducción del tipo auto, los lambdas genéricos siguen las reglas de la argumentación plantilla de deducción (que son similares, pero no idénticos en todos los aspectos). El código anterior es equivalente a esto.[13]

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 lambda[editar]

Las funciones lambda de C++11 las variables de captura declaran en su ámbito exterior por valor copia o por referencia. Esto significa que los miembros de un valor lambda no pueden ser solo tipos de movimiento.[14]​ C++14 permite miembros capturados para ser inicializados con expresiones arbitrarias. Esto permite que tanto la captura por valor movimiento y declarando miembros arbitrarias de la lambda, sin tener una variable correspondiente con nombre en un ámbito exterior.[8]

Esto se realiza mediante el uso de una expresión inicializador.

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

La función lambda será return 1, que es valor fue inicializado con la captura declarada que deduce el tipo de la expresión de inicialización como auto.

Esto puede ser usado para capturar por el movimiento, a través del uso de la función std::move.

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

El atributo [[deprecated]][editar]

El atributo deprecated permite marcar una entidad [[deprecated]], lo que lo hace todavía legal para utilizar, pero pone a los usuarios sobre aviso de que su uso no se recomienda y puede causar un mensaje de advertencia a imprimir durante la compilación. Una cadena de caracteres literal opcional puede aparecer como argumento de deprecated, para explicar la razón de la depreciación y/o sugerir un reemplazo.

[[deprecated]] int f();

[[deprecated("g() es el hilo inseguro, en su lugar, h()")]]
void g( int& x );

void h( int& x );

void prueba() {
  int a = f(); // Advertencia: 'f' está en desuso.
  g(a); // Advertencia: 'g' está en desuso: g() es el hilo inseguro, en su lugar, h().
}

Nuevas funciones de la biblioteca estándar[editar]

Exclusiones mutuas compartidas y de bloqueo[editar]

C++14 agrega un tiempo silenciado compartido y un acompañante compartido de tipo de bloqueo[15][16]

Búsqueda heterogénea en contenedores asociativos[editar]

La biblioteca estándar de C++ define cuatro clases de contenedores asociativos. Estas clases permiten al usuario las operaciones de búsqueda un valor basado en un valor de ese tipo. Los contenedores mapa permiten al usuario especificar una clave y un valor, donde las operaciones de búsqueda se realiza por clave y devuelve un valor, sin embargo, la búsqueda se realiza siempre por el tipo de clave específica, si es la clave como en los mapas o el valor como en juegos.

C++14 permite la búsqueda que se realiza a través de un tipo arbitrario, siempre y cuando el operador de comparación pueda comparar ese tipo con el tipo de clave real.[17]​ Esto permitiría un mapa de std::string a algún valor para comparar contra un const char* o cualquier otro tipo para el que una operator sobrecarga está disponible.

Para preservar la compatibilidad hacia atrás, búsqueda heterogénea solo se permite cuando el comparador dada al contenedor asociativo permite. Las clases de la biblioteca estándar std::less<> y std::greater<> están aumentada para permitir las operaciones de búsqueda heterogéneo.[18]

Definiciones estándar por el usuario de literales[editar]

C++11 definió la sintaxis para sufijos literales definidos por el usuario, pero la biblioteca estándar no utilizó ninguno de ellos. C++14 agrega los siguientes literales estándar.[17]

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

Los dos literales "s" no interactúan, como la cadena solo se opera en literal string, y uno para el segundo opera solo en números.[19]

Tuple aborda a través del tipo[editar]

El tipo std::tuple introducido en C++11 permite un conjunto de valores escritos para ser indexado por una constante entera en tiempo de compilación, C++14 se extenderá esto para permitir ir a buscar de una tupla por tipo en lugar de por el índice.[17]​ Si la tupla tiene más de un elemento del tipo, un error en tiempo de compilación resultará.[20]

tuple<string, string, int> t("foo", "bar", 7);
int i = get<int>(t); // i == 7
int j = get<2>(t); // j == 7
string s = get<string>(t); // Error en tiempo de compilación debido a la ambigüedad.

Operaciones de biblioteca pequeñas[editar]

std::make_unique se puede utilizar como std::make_shared para std::unique_ptr objetos.[8]

std::integral_constant ganará un operator() sobrecarga para devolver el valor constante.[17]

Las funciones globales std::begin/std::end serán aumentadas con las funciones std::cbegin/std::cend, que volverán iteradores constantes comienzo/fin del rango.

Véase también[editar]

Referencias[editar]

  1. Herb Sutter (18 de agosto de 2014). «We have C++14!». Consultado el 18 de agosto de 2014. 
  2. «ISO/IEC 14882:2014». International Organization for Standardization (en inglés). Consultado el 6 de diciembre de 2017. 
  3. «El proyecto del comité, la norma para la programación del lenguaje C++» (PDF). ISO. 15 de mayo de 2013. 
  4. Sutter, Herb (18 de agosto de 2014), Tenemos C++ 14!, consultado el 18 de agosto de 2014 .
  5. a b c d e Wong, Michael (30 de abril de 2013). «La vista de la norma C++, Abril de 2013 Part 3». C/C++ Café. Consultado el 14 de junio de 2013. 
  6. a b c Merrill, Jason (17 de abril de 2013). «N3638 Tipo de retorno deducción para las funciones normales (Revisión 5)». Consultado el 14 de junio de 2013. 
  7. Smith, Richard (18 de abril de 2013). «N3652 Relajado sobre limitaciones de las funciones constexpr». 
  8. a b c d Sutter, Herb (20 de abril de 2013). «El informe de inspiración: ISO C++ Reunión, primavera norte de 2013». isocpp.org. Consultado el 14 de junio de 2013. 
  9. Dos Reis, Gabriel (19 de abril de 2013). «N3651 Plantillas variables (Revisión 1)» (PDF). 
  10. Vandevoorde, Daveed; Voutilainen, Ville (17 de abril de 2013). «N3653 Inicializadores del miembro y agregados». 
  11. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=23479#c29
  12. Crowl, Lawrence; Smith, Richard; Snyder, Jeff; Vandevoorde, Daveed (25 de septiembre de 2013). «N3781 Comillas simples como agrupador de dígitos». 
  13. Faisal, Vali; Sutter, Herb; Abrahams, Dave (19 de abril de 2013). «N3649 Expresiones lambda genéricas, es decir, polimórficas (Revision 3)». 
  14. «Captura de movimiento en lambda». Stack Overflow. 
  15. Wong, Michael (30 de abril de 2013). «La vista de la norma C++ Reunión Abril de 2013 Parte 3». C/C++ Café. Consultado el 14 de junio de 2013. 
  16. Howard, Hinnant; Vollmann, Detlef; Boehm, Hans (19 de abril de 2013). «N3659 Bloqueado compartido C++ (Revisión 2)». 
  17. a b c d Wong, Michael (26 de abril de 2013). «La vista de la norma C++ Reunión Abril de 2013 Parte 2». C/C++ Café. Consultado el 14 de junio de 2013. 
  18. «N3657 Adición de las operaciones de búsqueda de comparación heterogéneo a asociativo contenedores (revisión 4)». 19 de Mar<o de 2013. 
  19. Peter, Sommerlad (18 de abril de 2013). «N3642 Literales definidos por el usuario para tipos de biblioteca estándar (parte 1 - versión 4)» (PDF). 
  20. Spertus, Mike (19 de abril de 2013). «N3670 Redacción para abordar tuplas por tipo: Revisión 2». 

Enlaces externos[editar]