Chapitre 7 : modèles #
Slides #
Version imprimable (faire CTRL+P)
Exemples #
1242.2_07.01_FunctionTemplateMaxi
Time.h
#include <iostream>
class Time
{
public:
Time(int h = 0, int m = 0, int s = 0) : m_hour(h), m_minute(m), m_second(s) {}
bool operator>(const Time& other) const
{
return toSeconds() > other.toSeconds();
}
int getHour() const { return m_hour; }
int getMinute() const { return m_minute; }
int getSecond() const { return m_second; }
friend std::ostream& operator<<(std::ostream& os, const Time& t);
private:
int toSeconds() const
{
return m_hour * 3600 + m_minute * 60 + m_second;
}
int m_hour{0};
int m_minute{0};
int m_second{0};
};
inline std::ostream& operator<<(std::ostream& os, const Time& t)
{
return os << t.m_hour << "h" << t.m_minute << "'" << t.m_second << "\"";
}main.cpp
#include <iostream>
#include "Time.h"
template <typename T>
T maxi(const T& a, const T& b)
{
return (a > b) ? a : b;
}
int main()
{
Time t1(9, 57, 59);
Time t2(9, 58, 37);
auto maxTime = maxi<Time>(t1, t2); // Explicit type specification
maxTime = maxi(t1, t2); // Automatic type deduction
// Time has operator<< but no std::formatter, so use std::cout
std::cout << maxTime << " = maxi(" << t1 << ", " << t2 << ")" << '\n';
return 0;
}1242.2_07.02_FunctionTemplateRectangle
main.cpp
#include <print>
#include <iostream>
class Rectangle
{
public:
Rectangle(double w = 0, double h = 0) : m_width(w), m_height(h) {}
bool operator>=(const Rectangle &other) const
{
return (m_width * m_height) >= (other.m_width * other.m_height);
}
friend std::ostream &operator<<(std::ostream &os, const Rectangle &r);
private:
double m_width{0};
double m_height{0};
};
std::ostream &operator<<(std::ostream &os, const Rectangle &r)
{
return os << "[" << r.m_width << ", " << r.m_height << "]";
}
template <typename T>
T maxi(const T &a, const T &b)
{
return (a >= b) ? a : b;
}
int main()
{
auto i1 = 3;
auto i2 = 4;
auto d1 = 2.34;
auto d2 = 2.35;
Rectangle r1(10, 20);
Rectangle r2(20, 30);
auto maxInt = maxi(i1, i2); // Automatic type deduction
auto maxDouble1 = maxi(d1, d2); // Automatic type deduction
auto maxDouble2 = maxi<double>(i1, d1); // Explicit: compiler can't deduce mixed types
// maxi(i1, d1); // Would not compile: ambiguous types
auto maxRectangle = maxi(r1, r2); // Rectangle must overload operator>=
std::println("maxi(int, int) : ({}, {}) --> {}", i1, i2, maxInt);
std::println("maxi(double, double) : ({}, {}) --> {}", d1, d2, maxDouble1);
std::println("maxi<double>(int, double): ({}, {}) --> {}", i1, d1, maxDouble2);
// Rectangle has operator<< but no std::formatter
std::cout << "maxi(Rectangle, Rectangle): (" << r1 << ", " << r2 << ") --> " << maxRectangle << '\n';
return 0;
}1242.2_07.03_ClassTemplateConversion
Wrapper.h
#include <iostream>
#include <typeinfo>
template <typename T>
class Wrapper
{
public:
Wrapper(const T &val = T()) : m_value(val) {}
// Cross-type converting constructor (not a copy constructor)
template <typename U>
Wrapper(const Wrapper<U> &other) : m_value(static_cast<T>(other.getValue())) {}
T getValue() const { return m_value; }
template <typename T2>
friend std::ostream &operator<<(std::ostream &os, const Wrapper<T2> &w);
private:
T m_value;
};
template <typename T2>
std::ostream &operator<<(std::ostream &os, const Wrapper<T2> &w)
{
return os << w.m_value << " (type: " << typeid(w.m_value).name() << ")";
}main.cpp
#include <iostream>
#include "Wrapper.h"
int main()
{
Wrapper<float> a(3.1f); // Value is float
Wrapper<int> b(a); // Value is int (converted from float)
Wrapper<float> f = a; // Copy constructor (same type)
// Wrapper has operator<< but no std::formatter
std::cout << a << '\n'; // Prints 3.1 (float)
std::cout << b << '\n'; // Prints 3 (int, truncated)
std::cout << f << '\n'; // Prints 3.1 (float)
return 0;
}1242.2_07.04_ClassTemplateNonType
Array.h
#include <print>
// Class template with non-type template parameter (compile-time constant)
template <typename T, int Size>
class Array
{
public:
Array() = default;
T getAt(int pos) const
{
if (pos >= 0 && pos < Size)
{
return m_data[pos];
}
// Invalid position: could throw an exception instead
return T{-1};
}
void setAt(int pos, T val)
{
if (pos >= 0 && pos < Size)
{
m_data[pos] = val;
}
}
int getSize() const { return Size; }
private:
T m_data[Size];
};main.cpp
#include <print>
#include "Array.h"
int main()
{
Array<double, 5> t1;
t1.setAt(0, 1.5);
using TabInt7 = Array<int, 7>;
TabInt7 t2;
std::println("Array of double, size = {}", t1.getSize());
std::println("Values outside bounds return -1\n");
for (int i = -1; i <= t1.getSize(); i++)
{
t1.setAt(i, i / 2.0);
std::print("{}\t", t1.getAt(i));
}
std::println("\n");
std::println("Array of int, size = {}", t2.getSize());
std::println("Values outside bounds return -1\n");
for (int i = -1; i <= t2.getSize(); i++)
{
t2.setAt(i, i / 2);
std::print("{}\t", t2.getAt(i));
}
std::println("");
return 0;
}1242.2_07.05_TemplateMemberSpecialization
main.cpp
#include <print>
template <typename T>
class Point
{
public:
Point(T x = 0, T y = 0) : m_x(x), m_y(y) {}
void show();
private:
T m_x;
T m_y;
};
template <typename T>
void Point<T>::show()
{
std::println("Point<T>::show() Coordinates: {} {}", m_x, m_y);
}
// Full specialization of member function for char
template <>
void Point<char>::show()
{
std::println("Point<char>::show() Coordinates: {} {}", static_cast<int>(m_x), static_cast<int>(m_y));
}
int main()
{
Point<int> ptInt(3, 5);
ptInt.show();
Point<char> ptChar('d', 'y');
ptChar.show();
Point<double> ptDbl(3.5, 2.3);
ptDbl.show();
return 0;
}1242.2_07.06_FunctionTemplateOverload
main.cpp
#include <print>
#include <cstring>
// Generic template function
template <typename T>
T minVal(T tab[], int n)
{
std::println("minVal() template");
auto min = tab[0];
for (int i = 1; i < n; i++)
{
auto current = tab[i];
if (current < min)
{
min = tab[i];
}
}
return min;
}
// Non-template overload (not a specialization) — preferred over template<>
// for char* because it participates correctly in overload resolution
const char *minVal(const char *tab[], int n)
{
std::println("minVal() overload for char*");
auto result = tab[0];
for (int i = 1; i < n; i++)
{
if (std::strcmp(tab[i], result) < 0)
{
result = tab[i];
}
}
return result;
}
int main()
{
int tabInt[5] = {5, 7, 2, 6, 9};
char tabChar[10] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};
const char *tabStr[] = {"Hello", "Bonjour", "Gruezi", "Buenos dias", "Konichiwa"};
std::println("minVal<int>({{5,7,2,6,9}}, 5): {}", minVal<int>(tabInt, 5));
std::println("minVal<char>(\"HelloWorld\", 10): {}", minVal<char>(tabChar, 10));
std::println("minVal({{\"Hello\",\"Bonjour\",...}}, 5): {}", minVal(tabStr, 5));
return 0;
}1242.2_07.07_OverloadResolutionOrder
main.cpp
#include <print>
// Non-template function
void f(int)
{
std::println("Non-template");
}
// Template function
template <typename T>
void f(T)
{
std::println("Template");
}
// Explicit specialization of the template for int
template <>
void f<int>(int)
{
std::println("Specialization f<int>");
}
int main()
{
f(1); // Calls the non-template version (exact match preferred)
f('c'); // Calls the template version (T = char)
f<>(1); // Calls the specialized template version (f<> forces template)
return 0;
}Série 7.1 #
Exercice 1 : modèle de fonction #
- Écrire un modèle de fonction
exchangeutilisant un paramètre de typeT - Réaliser un programme permettant de tester la fonction avec :
- deux valeurs entières
- deux valeurs réelles
- deux objets de type
Time(série 3.1)
Question : que doit implémenter la classe Time au minimum ?
Exercice 2 : paramètre de type et paramètre expressions #
Écrire un modèle de fonction sum muni d’un paramètre de type < T > et d’un paramètre expression.
La fonction aura deux paramètres :
- l’adresse d’un tableau dont les éléments sont de type quelconque (
float,int,Point); - le nombre d’éléments qu’il faut additionner.
Cette fonction retourne la somme des éléments indiqués par le deuxième paramètre.
On pourra tester cette fonction avec le programme ci-dessous :
int main ()
{
int intTable[] = {3, 5, 2, 1};
float floatTable[] = {2.5, 3.2, 1.8};
char charTable[] = { 'A', '0', 'i', 'o', 'u' };
Time timeTable[] = { {5, 10}, {3, 22} };
std::println("sum(intTable, 4): {}", sum (intTable, 4)); // type deduction
std::println("sum(floatTable, 3): {}", sum (floatTable, 3)); // type deduction
std::println("sum(charTable, 5): {}", sum (charTable, 5)); // type deduction
std::println("sum(timeTable, 2): {}", sum (timeTable, 2)); // type deduction
return 0;
}Série 7.2 #
Exercice 1 : modèle de classe #
Écrire une classe générique Vector qui permet de manipuler des données de n’importe quel type.
On utilisera un tableau par valeur.
Vector<float> myFloatVector;L’instruction ci-dessus va créer une variable myFloatVector de 3 composantes de type float.
Pour créer un vector d’une taille différente de 3, la création se fera comme suit :
Vector<int, 4> myIntVector;Indications #
Il faudra prévoir un constructeur par recopie, un opérateur = pour attribuer un vecteur à partir d’un autre de même type. Exemple :
myFloatVector2 = myFloatVector;Une autre surcharge de l’opérateur = permettra d’initialiser un vecteur avec une même valeur.
Dans cet exemple, tous les éléments de myFloatVector auront la valeur 5.5.
myFloatVector = 5.5;Il est également demandé d’implémenter l’opérateur [] pour protéger l’exécution contre les débordements de tableau.
L’opérateur de flux « sera également implémenté.
Reprenez la classe Point.
Que faut-il modifier ?
Le programme ci-dessous montre un exemple d’utilisation :
int main()
{
Vector<int, 4> vi = {37};
vi[3] = 5;
vi[2] = 2;
std::cout << vi;
Vector<double> vd; // 3 elements by default
vd[0] = 0.0;
vd[1] = 0.1;
vd[2] = 0.2;
std::cout << vd;
std::println("\nvd[-8] --> out of range: {}", vd[-8]); // clamped to first element
std::println("\nvd[12] --> out of range: {}", vd[12]); // clamped to last element
std::println("vd[12] = 99.99");
vd[12] = 99.99; // clamped to last element
std::println("vd[2]: {}", vd[2]);
Vector<double> vd3;
std::cout << vd;
vd3 = vd;
std::cout << vd3;
Vector<Point, 2> vpt = {Point(0.0, 0.0, "default")};
Vector<Point, 2> vpt2 = {Point(1.0, 2.0, "other")};
std::cout << vpt;
vpt = vpt2;
std::cout << vpt;
// Class Template Argument Deduction (C++17)
// No need to specify template arguments if deducible
Vector vpt3(vpt2);
std::cout << vpt3;
std::println("");
return 0;
}