Variables plantilla

Variables plantilla C++14

Variables plantilla C++14

Las plantillas de C++ son una herramienta que permite definir familias de funciones u objetos, el código de una plantilla es común a todos los miembros de la familia y por ello ayuda a crear un código más genérico y reusable haciendo de las plantillas C++ una de las herramientas más potentes del lenguaje.

Hasta la aprobación de C++14 existían dos tipos de plantillas:

  • Funciones plantilla: define una familia de funciones, tanto para funciones miembro de objetos como para cualquier otra función.
  • Objetos plantilla: define una familia de objetos, ya sea una clase, una estructura o una unión.

Tras la aprobación de C++14, un nuevo tipo de plantilla fue añadido al lenguaje:

  • Variables plantilla: define una familia de variables que pueden ser usadas como cualquier otra variable tradicional.

En este artículo pretende explicar las Variables plantilla, no se centra en las Funciones u Objetos plantilla. Aún así, para que la explicación sea completa se introducen varios de los conceptos de las plantillas en general (una explicación a fondo requeriría varios artículos), si no estás interesado en la introducción puedes consultar directamente el final del artículo.

Lee el resto de esta entrada

Biicode, acercando C++ a Java

¡C++ elige a biicode!

¡C++ elige a biicode!

Todos los programadores tenemos un lenguaje favorito que defendemos ciegamente frente a otros programadores especializados en un lenguaje diferente. Esto ha provocado desde hace años “sanas” rivalidades entre lenguajes populares; una de las rivalidades más airadas y famosas (por lo grande de sus comunidades) es la rivalidad entre C++ y Java.

En general considero que comparar C++ y Java es injusto para ambos lenguajes: cada uno de ellos tiene un ámbito de de uso determinado y brilla con luz propia usado adecuadamente; aún así no podemos evitar hacer comparaciones, y es al comparar que podemos ver que C++ se está “Javificando” a la vez que Java se “C++fica”.

Lee el resto de esta entrada

Biicode, moving C++ towards a Java-flavoured language

C++ chooses biicode!

C++ chooses biicode!

All coders have a favourite language, a language that we blindly defend against other coders specialized in other languages. This practice has lead to “battles” between popular languages; one of the most popular battles of this kind is the fight between C++ and Java, both of them having a pretty big community of users.

As a coder, I think that comparing C++ and Java isn’t fair for both languages: they both shine when used wisely; but mankind tends to make comparisons and when comparing C++ and Java we can observe that C++ is becoming a little Java-flavoured and Java is moving towards a C++-flavoured language.

Lee el resto de esta entrada

Inicializadores de miembros en agregados

Inicializadores miembro en agregados

El estándar C++14 permite que los objetos agregados puedan tener inicializadores en las variables miembro; para conocer la importancia de este cambio, vamos a recordar qué son los inicializadores en variables miembros y los agregados:

Inicializadores en variables miembro

A partir del estándar C++11 es posible inicializar los miembros de un objeto en lugar en que estos están declarados:

// Este objeto es valido a partir de C++11
struct objeto
{
char caracter = '0';
const int entero = 1;
static const long largo = 2l;
};

Esta característica ya existía en otros lenguajes de programación como Java o C# y aunque C++ ya permitía este tipo de inicialización, se limitaba a los miembros estáticos constantes:

// Este objeto es valido antes y despues de C++11
struct objeto
{
static const char caracter = '0';
static const int entero = 1;
static const long largo = 2l;
};

Lee el resto de esta entrada

Deducción de tipo de retorno en funciones

Deducción de tipo de retorno en funciones

Deducción de tipo de retorno en funciones

El estándar de C++ aprobado en 2011 añadió al lenguaje la capacidad de deducir los tipos de variables y expresiones. Para hacerlo posible se añadieron dos nuevas palabras clave: auto y decltype.

La palabra clave auto es un especificador de tipo que le indica al compilador que deberá deducir el tipo de la variable declarada en base al inicializador de la misma:

auto caracter = 'c'; // auto = char
auto entero = 12345; // auto = int
auto flotante = 1.23f; // auto = float
auto doble = 1.234; // auto = double
auto sin_signo = 1234u; // auto = unsigned int
auto largo = 1234L; // auto = long
auto larguisimo = 123LL; // auto = long long
auto superlativo = 12ULL; // auto = unsigned long long
// Error de compilacion! el tipo no puede
// deducirse sin un inicializador!
auto error;

La palabra clave decltype es un especificador de tipo que le indica al compilador que deberá usar el tipo con el que una expresión fue declarada (declared type) y usarlo para la variable que se esté declarando:

int entero;
unsigned sin_signo;
void funcion(int f) {};
decltype(entero) v1; // copia el tipo int
decltype(sin_signo) v2; // copia el tipo unsigned int
decltype(funcion) v3; // copia el tipo void(int)
decltype(1. + 2.) v4; // copia el tipo double
decltype(1u / 2.f) v5; // copia el tipo float
decltype(&entero) v6; // copia el tipo int *
decltype(&v6) v7; // copia el tipo int **
decltype(1, 2., '3') v8; // copia el tipo char

Lee el resto de esta entrada

¡constexpr desencadenadas!

constexpr desencadenadas

Las expresiones constantes de C++ son la herramienta que nos permite crear expresiones que puedan ser evaluadas en tiempo de compilación, a la vez que se asegura el tipo de las mismas incluso con tipos definidos por el usuario. Fueron introducidas en el lenguaje al aprobar el estándar C++11 y para hacerlas posibles se añadió un nuevo calificador con la palabra clave constexpr.

Al usar el nuevo calificador, se le indica al compilador que evalúe durante la compilación aquello situado a la derecha del mismo; esto hace posible el uso de expresiones en lugares donde antes no era posible usarlas:

template <unsigned int ITERACION> struct Fibonacci
{
static const unsigned int valor =
Fibonacci<ITERACION - 1>::valor +
Fibonacci<ITERACION - 2>::valor;
};
template <> struct Fibonacci<1>
{
static const unsigned int valor = 1;
};
template <> struct Fibonacci<0>
{
static const unsigned int valor = 0;
};
unsigned int diez() { return 10; }
// Los valores de plantilla necesitan ser constantes
// en tiempo de compilacion:
auto v1 = Fibonacci<10>::valor;
// Error de compilacion: diez() no es una constante
// en tiempo de compilacion
auto v2 = Fibonacci<diez()>::valor;
// La cantidad de elementos debe ser una constante
// en tiempo de compilacion:
int valores1[55] = { 0 };
// Error de compilacion: la variable v1 no
// es una constante en tiempo de compilacion:
int valores1[v1] = { 0 };

En el ejemplo usamos una plantilla que calcula un término de la sucesión de Fibonacci. La plantilla usa un valor-plantilla y eso hace que falle al compilar la línea 26, ya que la función diez() devuelve un valor en tiempo de ejecución y los valores-plantilla deben estar disponibles en tiempo de compilación.

Sucede igual en la línea 34: el compilador espera encontrar un valor que le permita saber el tamaño del array; pero se encuentra una variable (v1) y al no conocer el tamaño del array falla al compilar.

Si definimos tanto la función diez() como la variable v1 como constexpr, sus valores estarán disponibles en tiempo de compilación y se corregirán los problemas del ejemplo anterior:

constexpr unsigned int diez() { return 10; }
// Correcto! diez() se calcula en tiempo de compilacion:
constexpr auto v1 = Fibonacci<diez()>::valor;
// Correcto! v1 se calcula en tiempo de compilacion:
int valores1[v1] = { 0 };

Lee el resto de esta entrada

Lambda con captura generalizada y Lambdas genéricas

Captura generalizada en Lambdas y Lambdas genéricas

Las funciones Lambda de C++ son un atajo para definir objetos-función, fueron añadidas al lenguaje en el estándar C++11. En el lenguaje C++ entendemos como objeto-función aquél objeto que puede ser usado como si fuera una función, es decir, aquel que disponga de al menos un operador paréntesis, por ejemplo:

struct imprimidor
{
void operator()(int numero)
{
std::cout << numero << ": "
<< std::string(numero, '*') << '\n';
}
};

Que una vez declarado, el objeto-función imprimidor puede ser usado de la siguiente manera:

std::vector<int> f = { 1, 1, 2, 3, 5, 8, 13, 21 };
// Creamos el objeto-funcion
imprimidor imprime;
for (auto n : f)
{
// Usamos el objeto-funcion
imprime(n);
}

La Lambda capaz de hacer lo mismo que el objeto-función imprimidor sería:

// Creamos la Lambda
auto imprime = [](int numero) -> void
{
std::cout << numero << ": "
<< std::string(numero, '*') << '\n';
};
for (auto n : f)
{
// Usamos la Lambda
imprime(n);
}

La sintaxis de una Lambda puede parecer confusa a primera vista, pero en realidad es incluso más sencilla que un objeto-función. Una función Lambda está dividida en cuatro partes:

Lista de captura

Lista de parámetros Tipo de retorno Cuerpo
[]
(int numero)
-> void
{ … }

Lee el resto de esta entrada

Literales binarios y separadores de miles en C++

Literales binarios

Literales binarios

A partir del estándar C++14, el lenguaje dispone de un dos nuevos estilos de representar literales numéricos que pueden facilitar el uso de los mismos en cualquier programa.

Antes de hablar de estos nuevos estilos de literales numéricos, vamos a recordar los literales existentes en C++.

Lee el resto de esta entrada

C++14 ha sido aprobado

C++14 ha sido aprobado

C++14 ha sido aprobado

El 18 de agosto de 2014 el Comité de Estándares dio por aprobado el borrador final de C++14, con lo que este nuevo estándar (antes conocido como C++1y) pasa a formar parte de la historia de C++ como la cuarta revisión del lenguaje.

Los estándares previos se conocen como C++98 (aprobado en 1998), C++03 (aprobado en 2003) y C++11 (aprobado el 12 de agosto de 2011). Tres revisiones del estándar en 28 años (de 1983 a 2011), no parecen muchas revisiones para un lenguaje que se empezó a gestar en la década de los 80.

Otros lenguajes parecidos a C++ (aún siendo más jóvenes) han evolucionado más rápido. Por ejemplo Java (1991) y C# (1999) contaban con 8 y 5 revisiones en la fecha de aprobación de C++14.

Así que, tras la aprobación de C++11 el Comité de Estándares anunció su decisión dinamizar el lenguaje acelerando sus revisiones. El objetivo es alternar grandes revisiones (como C++98 y C++11) con revisiones más pequeñas enfocadas en corrección de errores y pequeñas mejoras (como C++03 y C++14).

Habiendo hecho un repaso de historia, veamos qué ofrece el nuevo estándar del lenguaje C++

Lee el resto de esta entrada

const contra #define

const contra #define

¿qué debo usar? ¿const o #define?

La evolución del lenguaje C++ a lo largo de los años, lo ha ido distanciando paulatinamente de su predecesor, el lenguaje C. Pese a este distanciamiento se han mantenido cercanos con el fin de conservar un cierto grado de compatibilidad. La cercanía entre estos dos lenguajes ha dado lugar a que en ocasiones, prácticas habituales del lenguaje C pasen a ser habituales en C++.

Por ejemplo, los programadores C suelen preferir el uso de la directiva de Preprocesador #define para definir valores constantes, mientras que en C++ existe un mecanismo equivalente con el uso de la palabra reservada const. Este artículo se centra en el uso constantes, y del uso de #define para crear valores constantes en código, no se mencionarán otros usos de  #define.

Preferir usar #define para crear constantes en lugar de usar variables const también es una costumbre entre algunos programadores de C++.

Pero ¿es esto una mala práctica? ¿cuándo debe usarse #define y cuándo const?
Lee el resto de esta entrada

Sutter’s Mill

Herb Sutter on software development

Katy's Code

Software development, reverse engineering and video games

Andrzej's C++ blog

Guidelines and thoughts about C++

C++Next

The next generation of C++