# Cours 4 - Fonctions

Où est Charlie le Bug ?

int i = 0;
while (i < 10);
{
    cout << i << endl;
    i++;
}
 
if (x = 5) { }
 
for (int j = 0; j < 10; j++)
    cout << j;
    cout << " done";
 
switch (choice) {
    case 1:
        doSomething();
    case 2:
        doOther();
}

Solution :

  1. Supprimer le ; après while (i < 10)( Sinon ça provoque une boucle infinie )
  2. Remplacer = par == : if (x == 5)( = is assignment, == is comparison )
  3. Ajouter des accolades : for (...) { cout << j; cout << " done"; }( only first line is in the loop (misleading indent )
  4. Ajouter break; après doSomething(); ( missing break — falls through to case 2 )
  5. Il manque un peu partout les std:: devant cout et endl

Fondamentaux des fonctions

Déclaration vs Définition de fonction

// Declaration (prototype) — tells the compiler "this function exists"
double power(double x, int n);
 
// Definition — provides the actual implementation. Note : a definition is also a declaration !
double power(double x, int n)
{
    double result {1.0};
    if (n >= 0)
    {
        for (int i {1}; i <= n; ++i)
            result *= x;
    }
    else
    {
        for (int i {1}; i <= -n; ++i)
            result /= x;
    }
    return result;
}
// This works — definition comes before the call
int add(int a, int b) {
    return a + b;
}
 
int main() {
    add(2, 3);  // compiler already knows about add()
}
// This fails — compiler hasn't seen add() yet
int main() {
    add(2, 3);  // error: 'add' was not declared
}
 
int add(int a, int b) {
    return a + b;
}
int add(int a, int b);  // declaration only — no body
 
int main() {
    add(2, 3);  // now the compiler knows the signature
}
 
int add(int a, int b) {  // definition comes later
    return a + b;
}

Tutoriel : Calcul de puissances

// Calculating powers
#include <iostream>
#include <format>
 
double power(double x, int n)
{
  double result{ 1.0 };
  if (n >= 0)
  {
    for (int i{ 1 }; i <= n; ++i)
      result *= x;
  }
  else // n < 0
  {
    for (int i{ 1 }; i <= -n; ++i)
      result /= x;
  }
  return result;
}
 
int main()
{
  // Calculate powers of 8 from -3 to +3
  for (int i{ -3 }; i <= 3; ++i)
    std::cout << std::format("{:10g}", power(8.0, i));
 
  std::cout << std::endl;
}

Sortie : 0.00195312 0.015625 0.125 1 8 64 512

Pourquoi les prototypes de fonctions sont importants

// This WON'T compile — power() is used before it's defined!
#include <iostream>
#include <format>
 
int main()
{
  for (int i {-3}; i <= 3; ++i)
    std::cout << std::format("{:10g}", power(8.0, i));  // ERROR: 'power' not declared
  std::cout << std::endl;
}
 
double power(double x, int n)
{
  // ... definition comes too late
}

Règle : Soit vous définissez les fonctions avant main(), soit vous déclarez un prototype avant main() et définissez la fonction après.
Règle plus réglée : utiliser des header files (.h ou .hpp ou .hxx) et les inclure, mais on n’a pas encore vu ça :)

Overload de fonctions Plusieurs fonctions peuvent partager le même nom si elles ont des listes de paramètres différentes :

int maximum(int a, int b) { return a > b ? a : b; }
double maximum(double a, double b) { return a > b ? a : b; }
// The compiler picks the right one based on argument types

Ceci dit, si la seule différence est le type de retour de la fonction, ça ne compilera pas. C’est le type ou le nombre de paramètres à la fonction overloadée qui compte.

int    compute(double x);    // OK
double compute(double x);    // error: cannot overload on return type alone
double compute(int x);          // overload 1
double compute(double x);       // overload 2
double compute(int x, int y);   // overload 3
 

Valeurs de paramètres par défaut

void greet(std::string name, std::string greeting = "Hello")
{
    std::cout << std::format("{}, {}!", greeting, name) << std::endl;
}
 
greet("Alice");           // "Hello, Alice!"
greet("Bob", "Bonjour"); // "Bonjour, Bob!"

Règle : Les paramètres par défaut doivent aller de droite à gauche :

void func(int a, int b = 5, int c = 10);   // OK
// void func(int a = 5, int b, int c);      // ERROR!
 
func(3) // valide, même chose que func(3,5,10) // même chose

Ma qué, pourquoi de droite à gauche me direz vous ? Parce que les appels de fonction en C++ associent les arguments aux paramètres de gauche à droite, par position. Le compilateur a donc besoin de savoir sans ambiguïté à quel paramètre correspond chaque argument.

Si on pouvait sauter un paramètre au milieu, le compilateur ne saurait pas ce qu’on veut dire :

// hypothétiquement, si le C++ permettait ceci (ce n'est pas le cas)
void f(int a = 1, int b, int c = 3);
 
f(10, 20);  // est-ce f(10, 20, 3) ou f(1, 10, 20) ? C'est ambigu

En imposant les valeurs par défaut de droite à gauche, chaque appel est sans ambiguïté — on remplit à partir de la gauche et les valeurs par défaut complètent à partir de la droite :

void f(int a, int b = 2, int c = 3);
 
f(10);         // f(10, 2, 3)
f(10, 20);     // f(10, 20, 3)
f(10, 20, 30); // f(10, 20, 30)

Il n’y a jamais de trou. Le C++ n’a pas d’arguments nommés (contrairement à Python avec f(c=30, a=10)), donc la position est la seule information dont le compilateur dispose.

Overload, valeurs par défaut et name mangling : Le compilateur traite les fonctions overloadées comme des fonctions complètement distinctes. En coulisses, le C++ utilise le "name mangling", i.e. il encode les types des paramètres dans le nom du symbole. Ainsi, compute(int) pourrait devenir quelque chose comme _Z7computei et compute(double) devient _Z7computed dans le code binaire compilé. On peut d'ailleurs observer cela avec nm ou c++filt sur un fichier objet compilé.

Évidemment, c’est du C++, donc c’est bourré d’exceptions et de subtilités pas évidents, comme :

const et les références comptent — f(int&) et f(const int&) sont des overloads distincts.

Cela dit, un const de premier niveau sur un paramètre passé par valeur ne compte pas : f(int) et f(const int) ont la même signature, puisque l’appelant ne peut pas faire la différence.

Les arguments par défaut peuvent créer des ambiguïtés. Si on a f(int x, int y = 0) et f(int x), un appel à f(5) est ambigu et ne compilera pas — même si techniquement ce sont deux fonctions distinctes.

Passage de paramètres

Passage par valeur

// Failing to modify the original value of a function argument
#include <iostream>
 
double changeIt(double it);    // Function prototype
 
int main()
{
    double it {5.0};
    double result {changeIt(it)};
 
    std::cout << std::format("After function execution, it = {}", it) << std::endl;    // Still 5.0!
    std::cout << std::format("Result returned is {}", result) << std::endl;             // 15.0
}
 
// Function that attempts to modify an argument and return it
double changeIt(double it)
{
    it += 10.0;                       // This modifies the COPY
    std::cout << std::format("Within function, it = {}", it) << std::endl;  // 15.0
    return it;
}

Sortie :

Within function, it = 15
After function execution, it = 5     ← Original unchanged!
Result returned is 15

Passage par référence (&)

void doubleValue(int& x)    // Note the & — this is a reference
{
    x = x * 2;              // This modifies the ORIGINAL
}
 
int main()
{
    int num {5};
    doubleValue(num);
    std::cout << std::format("{}", num) << std::endl;  // 10 — the original was modified!
}

Passage par référence constante (const &)

void printName(const std::string& name)  // Can read, but cannot modify
{
    std::cout << std::format("Name: {}", name) << std::endl;
    // name = "Changed";  // ERROR: name is const!
}

En résumé Quand utiliser quoi :

MéthodeÀ utiliser quand…Exemple
Passage par valeurTypes simples (int, double, bool) que vous n’avez pas besoin de modifiervoid print(int x)
Passage par référence &Vous devez modifier l’originalvoid swap(int& a, int& b)
Passage par const ref const&Types volumineux que vous avez seulement besoin de lirevoid print(const std::string& s)

Questions :

  • Est-ce que x, dans les cas 2 ou 3, est de type int ?
  • Quel est le “scope” de la variable x passée en paramètre à la fonction f?

Lab 1 : Fonction swap

#include <iostream>
#include <format>
 
void swap(int& a, int& b)
{
    int temp {a};
    a = b;
    b = temp;
}
 
int main()
{
    int x {10}, y {20};
    std::cout << std::format("Before: x={}, y={}", x, y) << std::endl;
    swap(x, y);
    std::cout << std::format("After:  x={}, y={}", x, y) << std::endl;
}

Lab 2 : ColorLab

Fichiers multiples, Headers, struct et Passage de Paramètres

Un Laboratoire de Couleurs On va construire un petit système de mélange de couleurs RGB. Il nous permettra de créer des couleurs, ajuster leurs composantes rouge, vert et bleu, mélanger deux couleurs ensemble, et afficher les résultats directement dans le terminal avec de la couleur. En chemin, on verra le pass-by-value, le pass-by-reference, et les const references en action.

Les couleurs RGB sont parfaites pour cet exercice : chaque composante (R, G, B) est un entier entre 0 et 255, ce qui nous oblige aussi à gérer le clamping (s’assurer qu’on ne dépasse jamais ces bornes).

Jusqu’ici, vous avez tout écrit dans un seul fichier main.cpp. Ça fonctionne bien pour les petits programmes, mais imaginez que vous avez 50 fonctions — faire défiler des milliers de lignes devient un cauchemar.

En développement C++ professionnel, on organise le code comme ceci :

FichierRôle
main.cppContient main() et la logique principale
colors.hHeader file — contient les déclarations (prototypes) des fonctions
colors.cppSource file — contient les définitions (implémentations) des fonctions

Pensez-y comme un atelier de peinture :

  • Le header file (.h) est le catalogue de l’atelier — il vous dit ce qui est disponible (noms des fonctions, paramètres, types de retour).
  • Le source file (.cpp) est l’arrière-boutique — c’est comment les choses sont réellement préparées (le corps des fonctions).
  • main.cpp est l’artiste — il consulte le catalogue et passe ses commandes.

Deux autres nouveaux concepts qu’on n’a pas encore vu

1 - Les struct en C++

Le problème Sans struct, si on veut représenter une couleur avec seulement le types de base, on a, par exemple, besoin de plusieurs variables indépendantes :

int r1 = 220, g1 = 60, b1 = 10;      // Rouge Soleil
int r2 = 10,  g2 = 80, b2 = 210;     // Bleu Océan

Rien ne lie r1, g1 et b1 entre eux. Si on ajoute un nom, ça fait 4 variables par couleur.
Les fonctions deviennent lourdes :

void mixColors(int r1, int g1, int b1,
               int r2, int g2, int b2,
               int& outR, int& outG, int& outB);   // 9 paramètres !

La solution : struct Une struct regroupe des données qui vont ensemble dans un seul type :

struct Color {
    std::string name;
    int r, g, b;
};

On crée ensuite une variable de type Color comme n’importe quelle autre variable :

Color rougeSoleil{"Rouge Soleil", 220, 60, 10};
Color bleuOcean{"Bleu Océan", 10, 80, 210};

On accède aux membres avec le . :

std::cout << rougeSoleil.name;   // "Rouge Soleil"
std::cout << rougeSoleil.r;      // 220

Avant / Après

Sans structAvec struct
int r1, g1, b1;Color soleil;
4 variables par couleur1 variable qui contient tout
mixColors(r1, g1, b1, r2, g2, b2, outR, outG, outB)mixColors(soleil, ocean)
9 paramètres2 paramètres

En résumé

  • Une struct regroupe des variables liées dans un type personnalisé
  • On accède aux membres avec .
  • Ça rend le code plus lisible et les fonctions plus simples

2 - Affichage de couleurs dans le terminal On utilisera les ANSI escape codes — une fonctionnalité intégrée dans les terminaux Mac et Linux, sans aucune librairie externe. Ça nous permet d’afficher du texte coloré directement.

// \033 is interpreted as the octal escape for the ESC character (ASCII 27), which is what triggers ANSI color codes in the terminal
// Exemple d'ANSI escape code pour afficher du texte en couleur RGB :
 
int main()
{
    // --- Créer deux couleurs (pass by value) ---
    int r1 = 220, g1 = 60, b1 = 10;     // Rouge Soleil
    std::cout << "\033[38;2;" << r1 << ";" << g1 << ";" << b1 << "m";
    std::cout << "████████";   // Bloc de couleur
    std::cout << "\033[0m";    // Réinitialiser la couleur
}

Ce que vous verrez dans le terminal cmd.exe :

☝🏼

CLion sur Windows malheureusement a un terminal qui ne supporte pas bien les couleurs. Pour bien voir le résultat, exécutez colorlab.exe, qui se trouve dans votre dosser cmake-build.debug, dans un command line (cmd.exe).

1 - Créer le projet dans CLion

  1. Créez un nouveau dossier appelé ColorLab.
  2. Ouvrez VSCode et “Open folder” de ColorLab
  3. À l’intérieur de ce dossier, créez ces** fichiers** :
  • main.cpp

  • colors.h

  • colors.cpp

  • CMakeLists.txt

☝🏼

Dans CLion, ajoutez le toolchain MinGW (Default) dans Settings → Build, Execution, Deployments → Toolchains

Votre arborescence devrait ressembler à ceci :

ColorLab/
├── main.cpp
├── colors.h
└── colors.cpp
└── CMakeLists.txt

2 - Configurez le fichier CMakeLists.txt

cmake_minimum_required(VERSION 4.0)
project(colorlab LANGUAGES CXX)
 
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
add_executable(colorlab main.cpp colors.cpp colors.h)
 
if(MSVC)
    target_compile_options(colorlab PRIVATE /utf-8)
elseif(MINGW)
    target_compile_options(colorlab PRIVATE -pedantic -finput-charset=UTF-8 -fexec-charset=UTF-8)
    target_link_options(colorlab PRIVATE -static)
endif()

☝🏼

Il se peut que vous ayez à faire un “Load CMake Project” si vous voyez le warning suivant “Project not configured” au-dessus de votre fichier CMakeLists.txt

à ce moment-là, cliquez sur Fix… et choisissez “Load CMake Project”

3 - Le Header File (colors.h) Le header file ne contient que les déclarations (prototypes). Il dit au compilateur : « ces fonctions existent, et voici à quoi elles ressemblent. »

// colors.h
// ========
// Déclarations pour les fonctions du Laboratoire de Couleurs.
 
#ifndef COLORS_H
#define COLORS_H
 
#include <string>
 
// Une couleur RGB nommée.
struct Color {
    std::string name;
    int r, g, b;
};
 
// Vérifie si les composantes sont dans la plage [0-255].
// BY CONST REFERENCE — lecture seule, pas de copie de la struct.
bool isValid(const Color& c);
 
// Garde une valeur dans la plage [0, 255].
// BY REFERENCE — on modifie la variable originale.
void clamp(int& value);
 
// ⚠️ PIÈGE — version cassée de clamp !
// BY VALUE — la modification reste locale, l'original ne change PAS.
void clampBroken(int value);
 
// Augmente les composantes RGB d'une couleur.
// 'c' est BY REFERENCE — on modifie la couleur originale.
// Les amounts sont BY VALUE — un int est petit, pas besoin de référence.
void add(Color& c, int redAmount, int greenAmount, int blueAmount);
 
// Diminue les composantes RGB d'une couleur.
// Même logique : BY REFERENCE pour 'c', BY VALUE pour les amounts.
void remove(Color& c, int redAmount, int greenAmount, int blueAmount);
 
// Mélange deux couleurs (version ints) — 9 paramètres !
// Les 6 premiers sont BY VALUE — on ne veut pas altérer les originaux.
// Les 3 derniers sont BY REFERENCE — c'est là qu'on écrit le résultat.
void mixColors(int r1, int g1, int b1,
               int r2, int g2, int b2,
               int& outR, int& outG, int& outB);
 
// Mélange deux couleurs (version struct) — OVERLOAD de la fonction ci-dessus.
// Même nom, signature différente → le compilateur choisit la bonne version.
// Entrées BY CONST REFERENCE — on ne modifie pas les originaux.
// Retour PAR VALEUR — efficace grâce au RVO (Return Value Optimization).
Color mixColors(const Color& a, const Color& b);
 
// Affiche une couleur dans le terminal avec un bloc coloré ANSI.
// BY CONST REFERENCE — efficace (évite la copie du string + ints),
//   et on promet de ne pas modifier la couleur.
void displayColor(const Color& c);
 
#endif // COLORS_H
 
 

C’est quoi ce #ifndef / #define / #endif ?

On appelle ça des include guards. Ils empêchent le header d’être traité plus d’une fois s’il est accidentellement inclus à plusieurs endroits. C’est une pratique standard — faites-le toujours dans vos header files.

Comment ça fonctionne :

  1. La première fois que le compiler voit #include "colors.h", COLORS_H n’est pas encore défini.
  2. Donc il le définit (#define COLORS_H) et traite tout ce qui se trouve à l’intérieur.
  3. S’il rencontre #include "colors.h" une deuxième fois, COLORS_H est déjà défini, donc il saute tout. Pas de déclarations en double.

4 - Le Source File (colors.cpp) Le source file contient les définitions (mises en œuvre). C’est ici que la vraie logique se trouve.

// colors.cpp
// ==========
// Définitions pour les fonctions du Laboratoire de Couleurs.
 
#include "colors.h"
#include <iostream>
 
// ------------------------------------------------------------
// isValid — vérifie que les composantes sont dans [0, 255]
// 'c' est passé BY CONST REFERENCE — on lit sans copier
// ------------------------------------------------------------
bool isValid(const Color& c) {
    if (c.r < 0 || c.r > 255 || c.g < 0 || c.g > 255 || c.b < 0 || c.b > 255) {
        std::cout << "  ATTENTION : " << c.name << " a des valeurs hors plage !" << std::endl;
        return false;
    }
    return true;
}
 
// ------------------------------------------------------------
// clamp — garde une valeur entre 0 et 255
// BY REFERENCE — modifie l'original
// ------------------------------------------------------------
void clamp(int& value) {
    if (value < 0)   value = 0;
    if (value > 255) value = 255;
}
 
// ------------------------------------------------------------
// clampBroken — ⚠️ PIÈGE : version cassée de clamp
// BY VALUE — c'est une COPIE locale.
// La modification ne sort jamais de cette fonction !
// ------------------------------------------------------------
void clampBroken(int value) {
    if (value < 0)   value = 0;
    if (value > 255) value = 255;
    // 'value' est une copie → elle meurt ici, l'original est INCHANGÉ
}
 
// ------------------------------------------------------------
// add — augmente les 3 composantes et applique le clamp
// 'c' est BY REFERENCE — modifie la couleur originale
// Les amounts sont BY VALUE — petit type, copie pas chère
// ------------------------------------------------------------
void add(Color& c, int redAmount, int greenAmount, int blueAmount) {
    c.r += redAmount;
    c.g += greenAmount;
    c.b += blueAmount;
    clamp(c.r);
    clamp(c.g);
    clamp(c.b);
    std::cout << "  +RGB (" << redAmount << ", " << greenAmount << ", " << blueAmount
              << ") → R:" << c.r << "  G:" << c.g << "  B:" << c.b << std::endl;
}
 
// ------------------------------------------------------------
// remove — diminue les 3 composantes et applique le clamp
// Même logique que la fonction add
// ------------------------------------------------------------
void remove(Color& c, int redAmount, int greenAmount, int blueAmount) {
    c.r -= redAmount;
    c.g -= greenAmount;
    c.b -= blueAmount;
    clamp(c.r);
    clamp(c.g);
    clamp(c.b);
    std::cout << "  -RGB (" << redAmount << ", " << greenAmount << ", " << blueAmount
              << ") → R:" << c.r << "  G:" << c.g << "  B:" << c.b << std::endl;
}
 
// ------------------------------------------------------------
// mixColors (version ints) — 9 paramètres, pas très lisible...
// Les 6 premiers BY VALUE — les originaux ne changent pas
// Les 3 derniers BY REFERENCE — c'est là qu'on écrit le résultat
// ------------------------------------------------------------
void mixColors(int r1, int g1, int b1,
               int r2, int g2, int b2,
               int& outR, int& outG, int& outB) {
    std::cout << "  Mélange (version ints)..." << std::endl;
    outR = (r1 + r2) / 2;
    outG = (g1 + g2) / 2;
    outB = (b1 + b2) / 2;
}
 
// ------------------------------------------------------------
// mixColors (version struct) — OVERLOAD : même nom, signature différente
// BY CONST REFERENCE → 2 paramètres au lieu de 9, bien plus clair
// Retour PAR VALEUR — le compilateur optimise via RVO
// ------------------------------------------------------------
Color mixColors(const Color& a, const Color& b) {
    std::cout << "  Mélange de " << a.name << " + " << b.name << "..." << std::endl;
    return Color{
        a.name + " + " + b.name,
        (a.r + b.r) / 2,
        (a.g + b.g) / 2,
        (a.b + b.b) / 2
    };
}
 
// ------------------------------------------------------------
// displayColor — affiche un bloc coloré ANSI dans le terminal
// BY CONST REFERENCE — évite la copie, garantit la lecture seule
// ------------------------------------------------------------
void displayColor(const Color& c) {
    std::cout << "  [" << c.name << "]  ";
    std::cout << "\033[38;2;" << c.r << ";" << c.g << ";" << c.b << "m";
    std::cout << "████████";
    std::cout << "\033[0m";
    std::cout << "   R:" << c.r << "  G:" << c.g << "  B:" << c.b << std::endl;
}
 

Points importants à noter

  • On écrit #include "colors.h" (avec des guillemets, pas des chevrons). Les guillemets disent au compiler de chercher d’abord dans le dossier du projet. Les chevrons (< >) sont pour les headers système/standard library.
  • Chaque signature de fonction ici doit correspondre exactement à la déclaration dans le header.
  • La fonction clamp() est appelée à l’intérieur des autres fonctions — c’est un bon exemple de réutilisation de code.

5 - Le fichier principal contenant la fonction point d’entrée main() main.cpp

// main.cpp
// ========
// Le Laboratoire de Couleurs — là où la magie opère.
 
#include "colors.h"
#include <iostream>
 
#ifdef _WIN32
#include <windows.h>
#endif
 
#ifdef _WIN32
void enableAnsiColors() {
    // Set console to UTF-8
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(CP_UTF8);
 
    // Enable ANSI escape sequences
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD dwMode = 0;
    GetConsoleMode(hOut, &dwMode);
    SetConsoleMode(hOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
#endif
 
 
int main() {
 
    #ifdef _WIN32
    enableAnsiColors();
    #endif
    
    // --- Créer deux couleurs ---
    Color soleil{"Rouge Soleil", 220, 60, 10};
    Color ocean{"Bleu Océan", 10, 80, 210};
 
    displayColor(soleil);     // const Color& → lecture seule, pas de copie
    displayColor(ocean);
    std::cout << std::endl;
 
    // --- Validation (const Color&) ---
    std::cout << "--- Test de validation ---" << std::endl;
    Color invalide{"Invalide", 300, -10, 50};
    if (!isValid(invalide)) {
        std::cout << "  Couleur rejetée !" << std::endl;
    }
    std::cout << std::endl;
 
    // --- PIÈGE : clampBroken vs clamp (by value vs by reference) ---
    std::cout << "--- clampBroken vs clamp ---" << std::endl;
    int testValue = 999;
 
    std::cout << "  Avant :       testValue = " << testValue << std::endl;
 
    clampBroken(testValue);     // BY VALUE → ne modifie PAS testValue
    std::cout << "  clampBroken : testValue = " << testValue
              << "  (inchangé ! la copie est morte)" << std::endl;
 
    clamp(testValue);           // BY REFERENCE → modifie testValue
    std::cout << "  clamp :       testValue = " << testValue
              << "  (modifié via référence)" << std::endl;
    std::cout << std::endl;
 
    // --- Modifier une couleur (Color&) ---
    std::cout << "--- Modification de Rouge Soleil ---" << std::endl;
    add(soleil, 0, 80, 0);       // Color& → modifie la struct originale
    remove(soleil, 0, 0, 5);
    displayColor(soleil);
    std::cout << std::endl;
 
    // --- Réinitialiser Rouge Soleil avant le mélange ---
    // Le mélange sera vraiment Rouge Soleil + Bleu Océan, pas la version modifiée
    soleil = Color{"Rouge Soleil", 220, 60, 10};
    std::cout << "--- Rouge Soleil réinitialisé pour le mélange ---" << std::endl;
    displayColor(soleil);
    std::cout << std::endl;
 
    // --- Mélange avec ints : 9 paramètres, 3 out-references ---
    std::cout << "--- Mélange (version ints — 9 paramètres) ---" << std::endl;
    int outR, outG, outB;
    mixColors(soleil.r, soleil.g, soleil.b,
              ocean.r, ocean.g, ocean.b,
              outR, outG, outB);
    std::cout << "  Résultat : R:" << outR << "  G:" << outG << "  B:" << outB << std::endl;
    std::cout << std::endl;
 
    // --- Mélange avec struct : même nom, 2 paramètres, bien plus clair ---
    std::cout << "--- Mélange (version struct — overload) ---" << std::endl;
    Color fusion = mixColors(soleil, ocean);    // le compilateur choisit l'overload
    displayColor(fusion);
    std::cout << std::endl;
 
    // --- Vérifier que les originaux n'ont pas changé après les mélanges ---
    std::cout << "--- Originaux intacts (const& les protège) ---" << std::endl;
    displayColor(soleil);
    displayColor(ocean);
    std::cout << std::endl;
 
    return 0;
}
 

Remarquez que main.cpp n’a besoin que de #include "colors.h" — il n’a pas besoin de savoir comment les fonctions fonctionnent, seulement leurs déclarations respectives.

6 - Compiler et exécuter C’est le marteau dans CLion pour compiler, le “Play” vert pour exécuter

6.2 - Sortie attendue

☝🏼

CLion sur Windows malheureusement a un terminal qui ne supporte pas bien les couleurs. Pour bien voir le résultat, exécutez colorlab.exe, qui se trouve dans votre dosser cmake-build-debug, dans un command line (cmd.exe).
Pour ce faire, vous pouvez naviguer dans Windows Explorer vers le dossier cmake-build-debug, right-click et choisir “Open Terminal”.

Ensuite, il suffit d’exécuter colorlab.exe.

Si PowerShell se plain, exécutez en précédant colorlab.exe d’un “.\”, donc:
.\colorlab.exe puis enter