Les User Defined Literals en C++11
Le C++11 a rajouté une fonctionnalité assez méconnue nommée user defined literals. Son nom peut paraître légèrement ambiguë parce qu’il s’agit plutôt de traiter des suffixes donnés aux types littéraux.
Avant toute chose, si vous utilisez Visual Studio vous pouvez déjà oublier cette fonctionnalité mais vous pouvez tout de même suivre le reste de l’article juste pour voir ce que vous êtes entrain de manquer (et éventuellement vous plaindre à Microsoft).
Cette fonctionnalité a donc pour but d’écrire des valeurs constantes et de faire des opérations sur ces dernières grâce à vos propres suffixes. Par exemple, on pourra écrire un code similaire :
auto color = 0x44fecb_color;
auto message = "OK"_message;
De quoi s’agit il ? Il s’agit d’implémenter des fonctions par
suffixe, ici nous avons utilisé _color
et
_message
.
Exemple avec les unités de stockage mémoire
Soit une fonction generate qui souhaite créer un fichier. Cette fonction pour être le plus simple, prend en entrée un entier déterminant le nombre d’octet à écrire. Cependant, en tant qu’utilisateur, il serait bien sympa de pouvoir donner cette taille par kilo octet ou giga octet selon l’utilisation.
À l’heure actuelle on peut déjà répondre à ce besoin en passant par une classe de base qu’on nommerait par exemple Byte puis des classes filles comme GigaByte, KiloByte, etc. C’est à peu près ce qui est fait pour les classes chrono de la bibliothèque C++. Par contre on peut pas dire que c’est le plus élégant. On va donc utiliser nos propres suffixes pour écrire ceci :
(500_ko); generate
Tout d’abord, implémentons cette version, pour rappel 1Ko == 1024 octets. Son implémentation :
constexpr unsigned operator "" _ko(unsigned long long int value)
{
return value * 1024;
}
Avec cette fonction, à chaque fois que je vais suffixer un entier
litéral par _ko
, l’expression me retournera un
unsigned
de mon entier multiplié par 1024.
unsigned bytes = 1_ko; // équivalent à 1 * 1024
Note, comme nous avons mis la fonction constexpr
, notre
1_ko
est même évalué à la compilation ce qui n’altère pas
du tout les performances :-).
Exemple de code complet avec son main
#include <iostream>
constexpr unsigned operator "" _ko(unsigned long long int value)
{
return value * 1024;
}
constexpr unsigned operator "" _mo(unsigned long long int value)
{
return value * 1048576;
}
constexpr unsigned operator "" _go(unsigned long long int value)
{
return value * 1073741824;
}
constexpr unsigned operator "" _to(unsigned long long int value)
{
return value * 1099511627776;
}
void generate(unsigned size)
{
/* Create a file of specified size */
std::cout << "Creating a file of " << size << " bytes" << std::endl;
}
int main()
{
(55_ko);
generate(1_go);
generate(1_ko + 2_ko);
generate
return 0;
}
Sortie du programme :
Creating a file of 56320 bytes
Creating a file of 1073741824 bytes
Creating a file of 3072 bytes