Adrianistán

Novedades de C++17

19/04/2017
Después de tres años de trabajo, C++17 ha sido finalmente estandarizado. Esta nueva versión de C++ incorpora y elimina elementos del lenguaje, con el fin de ponerlo al día y convertirlo en un lenguaje moderno y eficaz. El comité ISO de C++ se ha tomado muy en serio su labor, C++11 supuso este cambio de mentalidad, que se ha mantenido en C++14 y ahora en C++17, la última versión de C++.



Repasemos las noveades que incorpora C++17 respecto a C++14

if-init


Ahora podemos incluir una sentencia de inicialización antes de la condición en sentencias if y switch. Esto es particularmente útil si queremos operar con un objeto y desconocemos su validez.


// ANTES
Device dev = get_device();
if(dev.isOk()){
dev.hacerCosas();
}

// AHORA
if(Device dev = get_device(); dev.isOk()){
dev.hacerCosas();
}


Declaraciones de descomposición



Azúcar sintántico que permite mejorar la legibiliad en ciertas situaciones. Por ejemplo, en el caso de las tuplas, su uso se vuelve trivial.


// FUNCIÓN QUE DEVUELVE TUPLA
std::tuple<int, std::string> funcion();

// C++14
auto tup = funcion();
int i = std::get<0>(tup);
std::string s = std::get<1>(tup);

// C++17
auto [i,s] = funcion();



Esto funciona para multitud de estructuras de datos, como estructuras, arrays, std::array, std::map,...


std::map m = ...;
for(auto && [key, value] : m){

}


Deduction Guides



Ahora es menos necesario que nunca indicar los tipos en ciertas expresiones. Por ejemplo, al crear pares y tuplas:


// ANTES
auto p = std::pair<int,std::string>(42,"Adrianistan");

// AHORA
auto p = std::pair(42,"Adrianistan");


Esto por supuesto también sirve para estructuras y otras construcciones:


template<typename T>
struct Thingy
{
T t;
};

// Observa
Thingy(const char *) -> Thingy<std::string>;

Thingy thing{"A String"}; // thing.t es de tipo std::string


template auto




// ANTES
template <typename T, T v>
struct integral_constant
{
static constexpr T value = v;
};
integral_constant<int, 2048>::value
integral_constant<char, 'a'>::value

// AHORA
template <auto v>
struct integral_constant
{
static constexpr auto value = v;
};
integral_constant<2048>::value
integral_constant<'a'>::value


Fold expressions



Imagina que quieres hacer una función suma, que admita un número ilimitado de parámetros. En C++17 no se necesita apenas código.


template <typename... Args>
auto sum(Args&&... args) {
return (args + ... + 0);
}


Namespaces anidados



Bastante autoexplicativo


// ANTES

namespace A{
namespace B {
bool check();
}
}

// AHORA

namespace A::B {
bool check();
}


Algunos [[atributos]] nuevos



[[maybe_unused]]


Se usa para suprimir la advertencia del compilador de que no estamos usando una determinada variable.


int x = 5;
[[maybe_unused]] bool azar = true;
x = x + 10


[[fallthrough]]



Permite usar los switch en cascada sin advertencias del compilador.

switch (device.status())
{
case sleep:
device.wake();
[[fallthrough]];
case ready:
device.run();
break;
case bad:
handle_error();
break;
}


Variables inline



Ahora es posible definir variables en múltiples sitios con el mismo nombre y que compartan una misma instancia. Es recomendable definirlas en un fichero de cabecera para luego reutilizarlas en ficheros fuente.


// ANTES
// en una cabecera para que la usasen los demás
extern int x;

// solo en un fichero fuente, para inicializarla
int x = 42;
// AHORA

// en la cabecera
inline int x = 42;



if constexpr



Ahora es posible introducir condicionales en tiempo de compilación (similar a las macros #IFDEF pero mejor hecho). Estas expresiones con constexpr, lo que quiere decir que son código C++ que se evalúa en tiempo de compilación, no de ejecución.


template<class T>
void f (T x)
{
if constexpr(std:: is_integral <T>::value) {
implA(x);
}
else if constexpr(std:: floating_point <T>::value) {
implB(x);
}
else
{
implC(x);
}
}


std::optional



Tomado de la programación funcional, se incorpora el tipo optional, que representa un valor que puede existir o no. Este tipo ya existe en Rust bajo el nombre de Option y en Haskell como Maybe.


std::optional opt = f();
if(opt)
g(*opt);

// otra opción de uso si queremos proveer de un reemplazo
std::optional opt = f();
std::cout << opt.value_or(0) << std::endl;


std::variant



Descritas como las unions pero bien hechas. Pueden contener variables de los tipos que nosotros indiquemos.


std::variant<int, double, std::vector> precio; // precio puede ser un int, un double o un std::vector

// comprobar si el valor en un variant es de un determinado tipo
if(std::holds_alternative<double>(precio))
double x = std::get<double>(precio);


std::any



Si con std::variant restringimos los posibles tipos de la variable a los indicados, con std::any admitimos cualquier cosa.


std::any v = ...;
if (v.type() == typeid(int)) {
int i = any_cast<int>(v);
}


std::filesystem


Se añade a la librería estándar este namespace con el tipo path y métodos para iterar y operar con directorios. Dile adiós a las funciones POSIX o Win32 equivalentes.


#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;

void main(){
fs::path dir = "/";
dir /= "sandbox";
fs::path p = dir / "foobar.txt";
std::cout << p.filename() << "\n";
fs::copy(dir, "/copy", fs::copy_options::recursive);
}


Algoritmos en paralelo



Muchos de los algoritmos de STL ahora pueden ejecutarse en paralelo bajo demanda. Con std::execution::par indicamos que queremos que el algoritmo se ejecute en paralelo.


std::sort(std::execution::par, first, last);


¿Qué novedades se esperan en C++20?


Ya hemos visto lo que trae C++17. Ahora veremos que se espera que traiga C++20 en 2020.


Referencias:





 
Tags: c17 programacion tuplas novedades estandar linux c tutorial cpp std