Améliorations des templates en C++20
Les améliorations apportées aux templates rendent le C++20 plus cohérent et moins sujet aux erreurs lors de l'écriture de programmes génériques.
Constructeur conditionnellement explicite
Parfois, vous avez besoin d'une classe acceptant différents types en entrée. Par exemple, une classe DifferentType contenant un std::variant :
Exemple 1 — Classe avec std::variant
class DifferentType {
variant<bool, char, int, double, float, string> myVariant;
};Pour initialiser un DifferentType avec différents types, une solution consiste à rendre le constructeur générique. Cependant, cela pose un problème : un constructeur générique est un catch-all — il accepte n'importe quel type.
- Sans
explicit: les conversions implicites sont autorisées — on peut écrireImplicite imp = "texte". - Avec
explicit: seuls les appels explicites sont valides —Explicite exp{"texte"}. Les conversions implicites sont interdites.
Exemple 2 — Constructeur implicite vs explicite
#include <iostream>
#include <string>
using namespace std;
struct Implicite {
template <typename T>
Implicite(T t) {
cout << t << '\n';
}
};
struct Explicite {
template <typename T>
explicit Explicite(T t) {
cout << t << '\n';
}
};
int main() {
Implicite imp1 = "implicite"; // conversion implicite : OK
Implicite imp2("explicite"); // appel explicite : OK
Implicite imp3 = 10.5; // conversion implicite : OK
Implicite imp4(10.5); // appel explicite : OK
cout << '\n';
// Explicite exp1 = "implicit"; // ERREUR : conversion implicite interdite
Explicite exp2{"explicite"}; // appel explicite : OK
// Explicite exp3 = 2021; // ERREUR : conversion implicite interdite
Explicite exp4{2021}; // appel explicite : OK
return 0;
}implicite explicite 10.5 10.5 explicite 2021
explicit conditionnel (C++20)
En C++20, explicit peut être utilisé de manière conditionnelle : explicit(condition). Si la condition vaut true, le constructeur est explicite ; sinon, la conversion implicite reste autorisée.
is_same est un prédicat de compilation de la bibliothèque <type_traits>. Il est évalué à la compilation et retourne true si les types T et U sont identiques, false sinon.Exemple 3a — explicit conditionnel : conversions autorisées
#include <iostream>
#include <type_traits>
#include <typeinfo>
using namespace std;
struct Note {
template <typename T>
explicit(!is_same<T, double>::value) Note(T t) {
cout << typeid(t).name() << '\n';
}
};
void afficher(Note n) {}
int main() {
Note n1(15.50); // appel explicite avec double : OK
Note n2 = 15.50; // conversion implicite depuis double : OK (explicit = false)
afficher(n1);
afficher(17.5); // conversion implicite depuis double : OK
// afficher(true); // ERREUR : bool != double → explicit = true
// afficher("Bonsoir"); // ERREUR : const char* != double → explicit = true
return 0;
}Exemple 3b — explicit conditionnel : erreurs de compilation
int main() {
Note n1(15.50);
Note n2 = 15.50;
afficher(true); // tentative de conversion implicite depuis bool
afficher("Bonsoir"); // tentative de conversion implicite depuis const char*
return 0;
}code.cpp:21:14: error: could not convert 'true' from 'bool' to 'Note'
21 | afficher(true);
| bool
code.cpp:22:14: error: could not convert '(const char*)"Bonsoir"' from 'const char*' to 'Note'
22 | afficher("Bonsoir");
| const char*explicit(!is_same<T, double>::value) rend le constructeur explicite pour tout type différent de double. Seule la conversion implicite depuis double est donc autorisée.Paramètres de template sans type
C++ permet d'utiliser des non-types comme paramètres de template. Avant C++20, les non-types supportés étaient :
- entiers et énumérateurs
- pointeurs vers des objets, des fonctions et des attributs de classe
- références
lvalue std::nullptr_t
C++20 étend cette liste en ajoutant le support des types à virgule flottante, des types littéraux et des littéraux de chaîne de caractères.
Types à virgule flottante
Exemple 4 — Paramètres de template à virgule flottante
#include <iostream>
#include <typeinfo>
using namespace std;
template <double d>
auto getDouble() {
return d;
}
template <auto NonType>
auto getNonType() {
return NonType;
}
int main() {
auto d1 = getDouble<5.5>(); // spécialisation pour 5.5
auto d2 = getDouble<6.5>(); // spécialisation pour 6.5
auto i = getNonType<2017>();
cout << i << " " << typeid(i).name() << '\n'; // int
auto f = getNonType<2020.1f>();
cout << f << " " << typeid(f).name() << '\n'; // float
auto d = getNonType<2020.2>();
cout << d << " " << typeid(d).name(); // double
return 0;
}2017 i 2020.1 f 2020.2 d
getDouble<valeur>() génère une nouvelle fonction spécialisée pour la valeur donnée. Depuis C++17, le paramètre auto permet de déduire le type du non-type. C++20 étend cette déduction aux types à virgule flottante.Types littéraux
Un type littéral satisfait deux propriétés :
- Toutes ses classes de base et membres de données non statiques sont publics et non modifiables.
- Tous ces membres sont de types structurels ou des tableaux de ceux-ci.
constexpr pour pouvoir être utilisé comme paramètre de template sans type.Exemple 5 — Type littéral comme paramètre de template
struct NewType {
constexpr NewType(int) {}
};
template <NewType cl>
auto getClassType() {
return cl;
}
int main() {
auto c1 = getClassType<NewType(2021)>();
return 0;
}Littéraux de chaîne de caractères
Exemple 6 — Chaîne comme paramètre de template sans type
#include <iostream>
using namespace std;
template <int N>
class StringLiteral {
public:
char data[N];
constexpr StringLiteral(char const (&str)[N]) {
copy(str, str + N, data);
}
};
template <StringLiteral str>
class ClasseTemplate {};
template <StringLiteral str>
void FunctionTemplate() {
cout << str.data << '\n';
}
int main() {
ClasseTemplate<"chaine de caracteres"> cls;
FunctionTemplate<"chaine de caracteres">();
return 0;
}chaine de caracteres
StringLiteral est un type littéral grâce à son constructeur constexpr (ligne 9) qui prend une chaîne C en paramètre. Il peut ainsi être utilisé comme paramètre de template sans type pour ClasseTemplate et FunctionTemplate.
Discussion (0)
Soyez le premier à laisser un commentaire !
Laisser un commentaire
Votre commentaire sera visible après modération.