Programmation par Objets en C++
Cl ´ement PERNET
L3-MI, UFR IM2AG, Universit ´e Grenoble Alpes
Plan du cours
G ´en ´ericit ´e (8-9)
Plan
G ´en ´ericit ´e (8-9)
Notion de g ´en ´ericit ´e
Classes et fonctions g ´en ´eriques enC++
Specialisation
Meta-Programmation template Traits
SFINAE
Plan
G ´en ´ericit ´e (8-9)
Notion de g ´en ´ericit ´e
Classes et fonctions g ´en ´eriques enC++
Specialisation
Meta-Programmation template Traits
SFINAE
G ´en ´ericit ´e
C++ est un langage fortement typ ´e :
les fonctions doivent ˆetre d ´efinies avec desargumentset une valeur de retourayant untype d ´efini;
les classes ont desattributsayant un type d ´efini, les
m ´ethodessont des fonctions comme ci-dessus.
Probl `eme :
I pour des fonctions ayant le m ˆeme traitement, I des classes ayant la m ˆeme structure
avec des types diff ´erents: on doit dupliquer le code
Comment factoriser ce code ? via la g ´en ´ericit ´e
G ´en ´ericit ´e
C++ est un langage fortement typ ´e :
les fonctions doivent ˆetre d ´efinies avec desargumentset une valeur de retourayant untype d ´efini;
les classes ont desattributsayant un type d ´efini, les
m ´ethodessont des fonctions comme ci-dessus.
Probl `eme :
I pour des fonctions ayant le m ˆeme traitement, I des classes ayant la m ˆeme structure
avec des types diff ´erents: on doit dupliquer le code
Comment factoriser ce code ? via la g ´en ´ericit ´e
Programmation G ´en ´erique
Programme g ´en ´erique :
Lorsque qu’un m ˆeme code peut ˆetre utilis ´e avec des objets de types variables
Exemple : algo de tri d’objets (entier, chaines de caract `eres, ...)
Deux types de g ´en ´ericit ´e :
I lorsque au sein d’une m ˆeme instance, plusieurs types diff ´erents peuvent ˆetre manipul ´es sous une m ˆeme d ´enomination h ´eritage et polymorphisme
I lorsque le type est inconnu au moment de l’ ´ecriture, mais est fix ´e de fac¸on unique lors de l’instanciation de la classe ou l’appel de la m ´ethode. types param `etr ´es
On parle alors de classes ou de m ´ethodes g ´en ´eriques.
Plan
G ´en ´ericit ´e (8-9)
Notion de g ´en ´ericit ´e
Classes et fonctions g ´en ´eriques enC++
Specialisation
Meta-Programmation template Traits
SFINAE
Notion de classe et fonction g ´en ´erique
EnC++,
I les classes et I les fonctions
peuvent ˆetre g ´en ´eriques par rapport `a I une classe ou un type
I une valeur enti `ere
Notion de classe et fonction g ´en ´erique
d ´eclaration : template< ... >d ´eclarant le param `etre g ´en ´erique pr ´ec `ede la d ´eclaration et la d ´efinition
template <class Truc>
void f(Truc t);
template <class Machin>
class A;
d ´efinition :idem
template <class Type> void f(Type t){...} template <class Machin> class A{ Machin x; };
Instanciation explicite :
A<int> a; f<float> (2.0);
Instanciation implicite : quand le param `etre template peut ˆetre d ´eduit sans ambiguit ´e :
float x = 2.0;
f (x); // Type = float
Notion de classe et fonction g ´en ´erique
d ´eclaration : template< ... >d ´eclarant le param `etre g ´en ´erique pr ´ec `ede la d ´eclaration et la d ´efinition
template <class Truc>
void f(Truc t);
template <class Machin>
class A;
d ´efinition :idem
template <class Type>
void f(Type t){...}
template <class Machin>
class A{ Machin x; };
Instanciation explicite :
A<int> a; f<float> (2.0);
Instanciation implicite : quand le param `etre template peut ˆetre d ´eduit sans ambiguit ´e :
float x = 2.0;
f (x); // Type = float
Notion de classe et fonction g ´en ´erique
d ´eclaration : template< ... >d ´eclarant le param `etre g ´en ´erique pr ´ec `ede la d ´eclaration et la d ´efinition
template <class Truc>
void f(Truc t);
template <class Machin>
class A;
d ´efinition :idem
template <class Type>
void f(Type t){...}
template <class Machin>
class A{ Machin x; };
Instanciation explicite :
A<int> a;
f<float> (2.0);
Instanciation implicite : quand le param `etre template peut ˆetre d ´eduit sans ambiguit ´e :
float x = 2.0;
f (x); // Type = float
Notion de classe et fonction g ´en ´erique
d ´eclaration : template< ... >d ´eclarant le param `etre g ´en ´erique pr ´ec `ede la d ´eclaration et la d ´efinition
template <class Truc>
void f(Truc t);
template <class Machin>
class A;
d ´efinition :idem
template <class Type>
void f(Type t){...}
template <class Machin>
class A{ Machin x; };
Instanciation explicite :
A<int> a;
f<float> (2.0);
Instanciation implicite : quand le param `etre template peut ˆetre d ´eduit sans ambiguit ´e :
float x = 2.0;
f (x); // Type = float
Notion de classe g ´en ´erique
Exemple
Une fonction max g ´en ´erique
template<typename T>
T max (T a, T b){
return (a < b) ? b : a;
}
// aucun code n’est compil´e, tant que max n’est pas invoqu´e avec // une sp´ecialisation pour T
int main(){
int a=2,b=1;
float x = 2.1;
int c = max<int> (a,b); // max<int> est compil´e pour cet appel
int d = max (c,b); // sp´ecialisation implicite: T=int
float y = max (a,x); // erreur ambiguit´e float y = max (static_cast<float>(a),x); // OK float y = max<float> (a,x); // OK
}
Notion de classe g ´en ´erique
Exemple
Une classe repr ´esentant des couples d’objets (de m ˆeme type)
#include <iostream>
template<class Type>
class Couple{
private:
Type x, y ; // les deux elements du couple public:
Couple (Type premier, Type second) {x = premier; y = second ;}
void affiche () {
// suppose qu’un Type peut ˆetre pass´e `a un ostream std::cout<< "(" << x <<", " << y << ")" << std::endl;
}
Type getPremier() {return x;}
Type getDeuxieme() {return y;}
};
Utilisation de la classe g ´en ´erique
I La d ´eclarationtemplatene fait queretarderla connaissance du type g ´en ´erique au moment de l’instanciation.
I A la compilation, il n’y a plus d’inconnue : tous les types g ´en ´eriques sontr ´esolus: on connaˆıt leur instantiation.
I Lors de la d ´eclaration d’un objet de typeCouple, ondoit sp ´ecifier le type effectif correspondant `aType(pas de r ´esolution implicite) :
Couple<int> * ci; Couple<Point> cp;
I On sp ´ecifie aussi le type lors de l’appel au constructeur :
ci = new Couple<int>(27, 43);
I Mais pas pour l’appel :cp.affiche()(la r ´esolution de type a d ´ej `a eu lieu)
Utilisation de la classe g ´en ´erique
I La d ´eclarationtemplatene fait queretarderla connaissance du type g ´en ´erique au moment de l’instanciation.
I A la compilation, il n’y a plus d’inconnue : tous les types g ´en ´eriques sontr ´esolus: on connaˆıt leur instantiation.
I Lors de la d ´eclaration d’un objet de typeCouple, ondoit sp ´ecifier le type effectif correspondant `aType(pas de r ´esolution implicite) :
Couple<int> * ci;
Couple<Point> cp;
I On sp ´ecifie aussi le type lors de l’appel au constructeur :
ci = new Couple<int>(27, 43);
I Mais pas pour l’appel :cp.affiche()(la r ´esolution de type a d ´ej `a eu lieu)
Utilisation de la classe g ´en ´erique
I La d ´eclarationtemplatene fait queretarderla connaissance du type g ´en ´erique au moment de l’instanciation.
I A la compilation, il n’y a plus d’inconnue : tous les types g ´en ´eriques sontr ´esolus: on connaˆıt leur instantiation.
I Lors de la d ´eclaration d’un objet de typeCouple, ondoit sp ´ecifier le type effectif correspondant `aType(pas de r ´esolution implicite) :
Couple<int> * ci;
Couple<Point> cp;
I On sp ´ecifie aussi le type lors de l’appel au constructeur :
ci = new Couple<int>(27, 43);
I Mais pas pour l’appel :cp.affiche()(la r ´esolution de type a d ´ej `a eu lieu)
Plusieurs param `etres de type
#include<iostream>
template <class T, class U>
class Couple{
T x;
U y;
public:
Couple (T premier, U second){x = premier; y = second;}
T getPremier () {return x;}
U getSecond () {return y;}
void affiche () {
// suppose que T et U peuvent ˆetre pass´es `a un ostream std::cout<< x <<", "<< y<<std::endl;
} };
int main(){
int i = 3;
double d = 2.5 ;
Couple<int, double>* c1 = new Couple<int,double>(i,d);
c1->affiche() ;
int j = 4 ;
Couple<int,int>* c2 = new Couple<int,int>(i,j);
c2->affiche() ;
}
Les types d’arguments template
Les arguments template peuvent ˆetre :
des types ou des classes : template<class T>ou template<typename T>sont ´equivalent des types template :
template<template <class T> class U>
template<class T>
class Tableau{...};
template<template<class T> class Tab>
T somme(Tab<T> tab){ ... }
des nombres entiers : template<int n>
template<int n>
class ArbreNaire{ ... };
I Initialisation par d ´efaut possible : template<class T=float>
Remarque sur l’instanciation
I Le compilateur ne cherche `a instantier que ce dont il a effectivement besoin.
#include<iostream>
template<class T>
class A{
void f();
void g();
};
template<class T>
A<T>::f(){std::cout<<"coucou")<<std::endl;}
int main() {
A<int> a;
a.f();
}
Ce code compile alors quegn’est pas d ´efinie.
I Instantiation explicite : pour forcer le compilateur `a g ´en ´erer une instance :
template Couple<int,double>; //Force les instanciations de template Couple<float,double>;//ces classes
Remarque sur l’instanciation
I Le compilateur ne cherche `a instantier que ce dont il a effectivement besoin.
#include<iostream>
template<class T>
class A{
void f();
void g();
};
template<class T>
A<T>::f(){std::cout<<"coucou")<<std::endl;}
int main() {
A<int> a;
a.f();
}
Ce code compile alors quegn’est pas d ´efinie.
I Instantiation explicite : pour forcer le compilateur `a g ´en ´erer une instance :
template Couple<int,double>; //Force les instanciations de template Couple<float,double>;//ces classes
Le mot cl ´e
typenamePlusieurs significations :
I comme type d’arg. template :template<typename T>
strictrement ´equivalent `aclass
I Pour distinguer lestypesdesvariables
I La g ´en ´ericit ´e template introduit des identificateurs pour les types en plus de ceux pour les variables
I besoin de pr ´eciser au compilateur quand il s’agit de types
template<class T> class CorpsZpZ{
typedef T Element
static const Element one; static const Element zero; };
template<class Corps>
typename Corps::Element add (typename Corps::Element x, typename Corps::Element y){ // suppose que Corps d´efinisse un type Element
return x+y;
}
Le mot cl ´e
typenamePlusieurs significations :
I comme type d’arg. template :template<typename T>
strictrement ´equivalent `aclass I Pour distinguer lestypesdesvariables
I La g ´en ´ericit ´e template introduit des identificateurs pour les types en plus de ceux pour les variables
I besoin de pr ´eciser au compilateur quand il s’agit de types
template<class T>
class CorpsZpZ{
typedef T Element
static const Element one;
static const Element zero;
};
template<class Corps>
typename Corps::Element add (typename Corps::Element x, typename Corps::Element y){
// suppose que Corps d´efinisse un type Element return x+y;
}
Inconv ´enients de la g ´en ´ericit ´e template
S ´eparation du code : fronti `ere floue entre d ´eclaration et d ´efinition
I l’int ´egralit ´e du code doit ˆetre disponible au moment de la r ´esolution du param `etre I impossible de compiler un code g ´en ´erique
sans connaˆıtre le type du param `etre toute la d ´efinition doit figurer dans le.h on peut utiliser un.tppinclu par le.h
Compilation multiples du m ˆeme code : car impossible de reposer sur un binaire pr ´e-compil ´e
temps de compilation accru taille des ex ´ecutables accrue
Inconv ´enients de la g ´en ´ericit ´e template
S ´eparation du code : fronti `ere floue entre d ´eclaration et d ´efinition
I l’int ´egralit ´e du code doit ˆetre disponible au moment de la r ´esolution du param `etre I impossible de compiler un code g ´en ´erique
sans connaˆıtre le type du param `etre toute la d ´efinition doit figurer dans le.h on peut utiliser un.tppinclu par le.h Compilation multiples du m ˆeme code : car impossible de
reposer sur un binaire pr ´e-compil ´e temps de compilation accru taille des ex ´ecutables accrue
Plan
G ´en ´ericit ´e (8-9)
Notion de g ´en ´ericit ´e
Classes et fonctions g ´en ´eriques enC++
Specialisation
Meta-Programmation template Traits
SFINAE
Sp ´ecialisation des arguments template
But : donner une impl ´ementation pour une valeur sp ´ecifique du param `etre g ´en ´erique
Comment : template<>
template <class T>
void f (T t){std::cout << "Affichage g´en´erique" << std::endl;}
template<>
void f<int> (int t){
std::cout << "Affichage sp´ecialis´e int: " << t << std::endl; }
// ou par d´eduction implicite du param`etre template template<>
void f (float t){
std::cout << "Affichage sp´ecialis´e float: " << t << std::endl;
}
Sp ´ecialisation des arguments template
But : donner une impl ´ementation pour une valeur sp ´ecifique du param `etre g ´en ´erique
Comment : template<>
template <class T>
void f (T t){std::cout << "Affichage g´en´erique" << std::endl;}
template<>
void f<int> (int t){
std::cout << "Affichage sp´ecialis´e int: " << t << std::endl;
}
// ou par d´eduction implicite du param`etre template template<>
void f (float t){
std::cout << "Affichage sp´ecialis´e float: " << t << std::endl;
}
Sp ´ecialisation partielle de classes
Pour les classes : on peut sp ´ecialiser certains des param `etres template.
template<class T, class U, int n>
class A{
};
template <class T, int n>
class A<T, double, n>{
};
template <class T>
class A<T, T*, 2>{
};
Pas forc ´ement une r ´eduction du nombre d’aguments :
template<class E>
class C{ };
template<class E>
class C<E*> { } ; // sp´ecialisation partielle pour un type pointeur vers E
Specialisation partielle de classes
On peut ne sp ´ecialiser que certaines m ´ethodes d’une classe template :
#include <iostream>
template<class T>
struct A{
T t;
T getT(){return t;}
void affiche(){
std::cout << "classe A g´en´erique" << std::endl;
} };
template<>
void A<int>::affiche(){
std::cout << "classe A sp´ecialis´ee sur int t = " << t
<< std::endl;
}
Sp ´ecialisation partielle de m ´ethodes ?
Interdite
template<class U,class V>
U f(){V v; U u; ...; return u;}
template<class U>
U f<U,int>(){int v; U u; ...; return u;} //Erreur de compilation
I Sauf si le type template est utilis ´e comme type d’un argument
template<class U, class V>
void f(U u, V v){std::cout<<"generique"<<std::endl;} template<class U, class V>
void f<U,int>(U u, int v){ // Erreur de compilation std::cout<<"sp´ecialisation partielle"<<std::endl; }
template<class U>
void f(U u, int v){ // surcharge -> OK std::cout<<"surcharge"<<std::endl;
}
g ´en ´ericit ´e proche de la surcharge
Sp ´ecialisation partielle de m ´ethodes ?
Interdite
template<class U,class V>
U f(){V v; U u; ...; return u;}
template<class U>
U f<U,int>(){int v; U u; ...; return u;} //Erreur de compilation
I Sauf si le type template est utilis ´e comme type d’un argument
template<class U, class V>
void f(U u, V v){std::cout<<"generique"<<std::endl;} template<class U, class V>
void f<U,int>(U u, int v){ // Erreur de compilation std::cout<<"sp´ecialisation partielle"<<std::endl; }
template<class U>
void f(U u, int v){ // surcharge -> OK std::cout<<"surcharge"<<std::endl;
}
g ´en ´ericit ´e proche de la surcharge
Sp ´ecialisation partielle de m ´ethodes ?
Interdite
template<class U,class V>
U f(){V v; U u; ...; return u;}
template<class U>
U f<U,int>(){int v; U u; ...; return u;} //Erreur de compilation
I Sauf si le type template est utilis ´e comme type d’un argument
template<class U, class V>
void f(U u, V v){std::cout<<"generique"<<std::endl;}
template<class U, class V>
void f<U,int>(U u, int v){ // Erreur de compilation std::cout<<"sp´ecialisation partielle"<<std::endl;
}
template<class U>
void f(U u, int v){ // surcharge -> OK std::cout<<"surcharge"<<std::endl;
}
g ´en ´ericit ´e proche de la surcharge
Sp ´ecialisation partielle de m ´ethodes
I Si le param `etre template n’est pas utilis ´e en argument soit forcer l’utilisation d’arguments (passer un argument inutile)
soit passer par la sp ´ecialisation partielle des classes (via les classes-fonctions)
Plan
G ´en ´ericit ´e (8-9)
Notion de g ´en ´ericit ´e
Classes et fonctions g ´en ´eriques enC++
Specialisation
Meta-Programmation template Traits
SFINAE
Meta-Programmation template
Le m ´ecanisme de r ´esolution de la g ´en ´ericit ´e est en fait aussi puissant qu’un langage de programmation :
I r ´ecursivit ´e I tests I boucles I etc
C’est le compilateur qui ex ´ecute ce programme avant de figer le r ´esultat dans le binaire.
le compilateur peut ˆetre pris pour un interpr ´eteur de langage template !
Meta-programmation template
template<int N>
struct Fibonacci {
static const int val = Fibonacci<N-1>::val + Fibonacci<N-2>::val;
};
template<>
struct Fibonacci<0>{static const int val = 1;};
template<>
struct Fibonacci<1>{static const int val = 1;};
int main(){
std::cout << Fibonacci<8>::val <<std::endl;
}
va g ´en ´erer l’assembleur (g++ -S)
movl $34, %esi
leaq _ZSt4cout(%rip), %rdi call _ZNSolsEi@PLT
Meta-programmation template
template<int N>
struct Fibonacci {
static const int val = Fibonacci<N-1>::val + Fibonacci<N-2>::val;
};
template<>
struct Fibonacci<0>{static const int val = 1;};
template<>
struct Fibonacci<1>{static const int val = 1;};
int main(){
std::cout << Fibonacci<8>::val <<std::endl;
}
va g ´en ´erer l’assembleur (g++ -S)
movl $34, %esi
leaq _ZSt4cout(%rip), %rdi call _ZNSolsEi@PLT
Meta-Programmation template
Tests conditionnnels
Isur des variables enti `eres
#include <iostream>
template<int n, int p>
struct equal{constexpr bool val = false;};
template<int n>
struct equal<n,n>{constexpr bool val = true;};
int main(){
if (equal<2,3>::val){ std::cout<<"2=3"<<std::endl;}
if (equal<4,4>::val){ std::cout<<"4=4"<<std::endl;}
}
IMieux : sur des types (le r ˆeve de tout ´etudiant)
#include <iostream> using namespace std; template<class A, class B>
struct equal{static const bool val = false;}; template<class A>
struct equal<A,A>{constexpr bool val = true;}; int main(){
if (equal<int,char>::val){cout<<"int=long"<<endl;} if (equal<long,long>::val){cout<<"long=long"<<endl;}
}
Meta-Programmation template
Tests conditionnnels
Isur des variables enti `eres
#include <iostream>
template<int n, int p>
struct equal{constexpr bool val = false;};
template<int n>
struct equal<n,n>{constexpr bool val = true;};
int main(){
if (equal<2,3>::val){ std::cout<<"2=3"<<std::endl;}
if (equal<4,4>::val){ std::cout<<"4=4"<<std::endl;}
}
IMieux : sur des types (le r ˆeve de tout ´etudiant)
#include <iostream>
using namespace std;
template<class A, class B>
struct equal{static const bool val = false;};
template<class A>
struct equal<A,A>{constexpr bool val = true;};
int main(){
if (equal<int,char>::val){cout<<"int=long"<<endl;}
if (equal<long,long>::val){cout<<"long=long"<<endl;}
}
Appart ´e : les expressions constantes
I expressions pouvant ˆetre calcul ´ees `a la compilation via le sp ´ecificateurconstexpr
constexpr double d1 = 2.0/1.0; // OK
constexpr int n = std::numeric_limits<int>::max() ;
Peut aussi s’appliquer aux valeurs de retour de fonctions :
constexpr int factorial(int n){
return n <= 1 ? 1 : (n * factorial(n - 1)); }
template<int n>
struct constN{constN() { std::cout << n << ’\n’; } }; int main(){
std::cout << "4! = " ; constN<factorial(4)> out1;
}
Appart ´e : les expressions constantes
I expressions pouvant ˆetre calcul ´ees `a la compilation via le sp ´ecificateurconstexpr
constexpr double d1 = 2.0/1.0; // OK
constexpr int n = std::numeric_limits<int>::max() ;
Peut aussi s’appliquer aux valeurs de retour de fonctions :
constexpr int factorial(int n){
return n <= 1 ? 1 : (n * factorial(n - 1));
}
template<int n>
struct constN{constN() { std::cout << n << ’\n’; } };
int main(){
std::cout << "4! = " ; constN<factorial(4)> out1;
}
Les constantes int ´egrales
C++11 propose des classes pour repr ´esenter des constantes connues `a la compilation
template <class T, T v>
struct integral_constant { // Constant de type T et de valeur v static constexpr T value = v; // stockage de la valeur typedef T value_type; // le type de la valeur typedef integral_constant<T,v> type;// le type de cette classe constexpr operator T() { return v; }// cast pour utilisation
};
Exemple
Factorielle, nouvelle version :
using namespace std; template <unsigned int n>
struct facto : integral_constant<int,n*facto<n-1>::value>{}; template <>
struct facto<0> : integral_constant<int,1> {};
Les constantes int ´egrales
C++11 propose des classes pour repr ´esenter des constantes connues `a la compilation
template <class T, T v>
struct integral_constant { // Constant de type T et de valeur v static constexpr T value = v; // stockage de la valeur typedef T value_type; // le type de la valeur typedef integral_constant<T,v> type;// le type de cette classe constexpr operator T() { return v; }// cast pour utilisation
};
Exemple
Factorielle, nouvelle version :
using namespace std;
template <unsigned int n>
struct facto : integral_constant<int,n*facto<n-1>::value>{};
template <>
struct facto<0> : integral_constant<int,1> {};
Les constantes int ´egrales
Cas particulier, les constantes bool ´eennes sont pr ´ed ´efinies :
typedef integral_constant<bool,true> true_type;
typedef integral_constant<bool,false> false_type;
Exemple
Am ´elioration du test d’ ´egalit ´e de classes :
template <class A, class B>
class equal : public std::false_type {};
template <class A>
class equal<A,A> : public std::true_type {};
int main (){
if (equal<int,double>()) // ou equal<int,double>::value std::cout << "int == double" << std::endl;
else
std::cout << "int != double" << std::endl;
}
Plan
G ´en ´ericit ´e (8-9)
Notion de g ´en ´ericit ´e
Classes et fonctions g ´en ´eriques enC++
Specialisation
Meta-Programmation template Traits
SFINAE
Les traits
Un trait est une classe template, ne contenant que des typedef, et/ou des constantes connues `a la compilation.
I permettant de donner des informations compl ´ementaires au type de l’argument template
I d’aiguiller des fonctions ou des classes utilisant ce type I `a co ˆut 0 (c’est le compilateur qui travaille)
Exemple
#include <iostream> template <typename T>
struct is_pointer{constexpr bool value = false;}; template <typename T>
struct is_pointer<T*>{constexpr bool value = true;}; int main(){
if (is_pointer<int*>::value){
std::cout<<"je suis un pointeur"<<std:endl; }
}
Les traits
Un trait est une classe template, ne contenant que des typedef, et/ou des constantes connues `a la compilation.
I permettant de donner des informations compl ´ementaires au type de l’argument template
I d’aiguiller des fonctions ou des classes utilisant ce type I `a co ˆut 0 (c’est le compilateur qui travaille)
Exemple
#include <iostream>
template <typename T>
struct is_pointer{constexpr bool value = false;};
template <typename T>
struct is_pointer<T*>{constexpr bool value = true;};
int main(){
if (is_pointer<int*>::value){
std::cout<<"je suis un pointeur"<<std:endl;
}
}
Les traits
Utilisation pour compl ´eter l’information d’un type :
template<typename T>
struct type_descriptor{
typedef T type;
typedef T* ptr_t;
typedef T& ref_t;
};
Probl `eme
type_descriptor<int&>::ref_test une ref de ref erreur Sp ´ecialisation
template<typename T>
struct type_descriptor<T&>{ typedef T& type;
...
typedef T& ref_t;
};
Les traits
Utilisation pour compl ´eter l’information d’un type :
template<typename T>
struct type_descriptor{
typedef T type;
typedef T* ptr_t;
typedef T& ref_t;
};
Probl `eme
type_descriptor<int&>::ref_test une ref de ref erreur
Sp ´ecialisation
template<typename T>
struct type_descriptor<T&>{ typedef T& type;
...
typedef T& ref_t;
};
Les traits
Utilisation pour compl ´eter l’information d’un type :
template<typename T>
struct type_descriptor{
typedef T type;
typedef T* ptr_t;
typedef T& ref_t;
};
Probl `eme
type_descriptor<int&>::ref_test une ref de ref erreur Sp ´ecialisation
template<typename T>
struct type_descriptor<T&>{
typedef T& type;
...
typedef T& ref_t;
};
Plan
G ´en ´ericit ´e (8-9)
Notion de g ´en ´ericit ´e
Classes et fonctions g ´en ´eriques enC++
Specialisation
Meta-Programmation template Traits
SFINAE
SFINAE : Substitution Failure is Not An Error
Lors d’une r ´esolution de param `etre template : I Le compilateur passe en revue les diff ´erentes
sp ´ecialisations disponibles,
I supprime celles qui ne fonctionnent pas, I choisit celle qui est la plus sp ´ecifique
l’ ´echec d’une tentative de substitution n’est pas une erreur Exemple
template<class T>
void f(T a, typename T::type b){} template<class T>
void f(T a, typename T::autre_type b){}
struct A{typedef int type;}; int main(){
f (A(), 42);
// A::autre_type n’existe pas
// -> erreur de compil dans la 2e d´ef de f en faisant T=A // -> signifie juste que cette subst. template est invalide
}
SFINAE : Substitution Failure is Not An Error
Lors d’une r ´esolution de param `etre template : I Le compilateur passe en revue les diff ´erentes
sp ´ecialisations disponibles,
I supprime celles qui ne fonctionnent pas, I choisit celle qui est la plus sp ´ecifique
l’ ´echec d’une tentative de substitution n’est pas une erreur
Exemple
template<class T>
void f(T a, typename T::type b){} template<class T>
void f(T a, typename T::autre_type b){}
struct A{typedef int type;}; int main(){
f (A(), 42);
// A::autre_type n’existe pas
// -> erreur de compil dans la 2e d´ef de f en faisant T=A // -> signifie juste que cette subst. template est invalide
}
SFINAE : Substitution Failure is Not An Error
Lors d’une r ´esolution de param `etre template : I Le compilateur passe en revue les diff ´erentes
sp ´ecialisations disponibles,
I supprime celles qui ne fonctionnent pas, I choisit celle qui est la plus sp ´ecifique
l’ ´echec d’une tentative de substitution n’est pas une erreur Exemple
template<class T>
void f(T a, typename T::type b){}
template<class T>
void f(T a, typename T::autre_type b){}
struct A{typedef int type;};
int main(){
f (A(), 42);
// A::autre_type n’existe pas
// -> erreur de compil dans la 2e d´ef de f en faisant T=A // -> signifie juste que cette subst. template est invalide
}
Application :
std::enable_ifI Permet de provoquer une erreur de type selon une condition bool ´eenne
I Donc de d ´esactiver une sp ´ecialisation (par SFINAE) selon une condition
enable_ifse comporte comme l’impl ´ementation suivante :
template<bool Condition, typename T = void>
struct enable_if{};
template<typename T>
struct enable_if<true, T> {typedef T type;};
I Si la condition est vraie :enable_if<true>::typeest le typeT
I Sinon, il n’existe pas erreur
Application :
std::enable_ifUtilisation 1 : dans la signature
#include <type_traits>
#include <math.h>
template <class T>
typename std::enable_if <
std::is_floating_point <T>::value,T
>::type modulo (T x, T n){return fmod(x,n);}
template <class T>
typename std::enable_if<std::is_integral<T>::value,T>::type modulo (T x, T n){return x % n;
}
int main(){
int n = modulo (5,3);
float x = modulo (5.0,3.0);
}
Application :
std::enable_ifUtilisation 2 : comme argument template silencieux
#include <type_traits>
#include <math.h>
using namespace std;
template<class T,
typename U=typename enable_if<
is_integral<T>::value,T
>::type
>
bool estPair (T x){return !(x % 2); }