🔝 Retour au Sommaire
Niveau : Débutant
Prérequis : Section 2.1 (Installation des compilateurs), Section 2.7 (Introduction à std::print)
Objectifs : Savoir vérifier le support destd::printetstd::formatsur votre système, connaître l'état du support par compilateur et par librairie standard, et mettre en place une stratégie de repli si votre environnement n'est pas encore prêt.
std::print fait partie du standard C++23, mais un standard n'est qu'un document tant que les compilateurs ne l'implémentent pas. Le support de <print> dépend à la fois du compilateur (qui doit accepter la syntaxe C++23 et le mécanisme consteval de std::format_string) et de la librairie standard (qui doit fournir l'implémentation de std::print, std::println et std::format). Ces deux composants évoluent indépendamment, ce qui explique pourquoi un compilateur peut accepter -std=c++23 sans pour autant fournir <print>.
| Composant | Header | Disponible depuis | Statut GCC 15 |
|---|---|---|---|
std::format |
<format> |
GCC 13 / libstdc++ 13 | ✅ Complet |
std::print / std::println |
<print> |
GCC 14 / libstdc++ 14 | ✅ Complet |
std::format_string (vérification compile-time) |
<format> |
GCC 14 | ✅ Complet |
Formateurs chrono (std::chrono::*) |
<chrono> + <format> |
GCC 14 | ✅ Complet |
Formateurs de ranges (std::vector, etc.) |
<format> |
GCC 15 | ✅ Complet |
Avec GCC 15 sur Ubuntu, std::print est pleinement fonctionnel. C'est la configuration de référence de cette formation :
g++ -std=c++23 -Wall -Wextra hello_print.cpp -o hello_print| Composant | Header | Disponible depuis | Statut Clang 20 |
|---|---|---|---|
std::format |
<format> |
Clang 17 / libc++ 17 | ✅ Complet |
std::print / std::println |
<print> |
Clang 19 / libc++ 19 | ✅ Complet |
std::format_string (vérification compile-time) |
<format> |
Clang 17 | ✅ Complet |
| Formateurs chrono | <chrono> + <format> |
Clang 19 | ✅ Complet |
| Formateurs de ranges | <format> |
Clang 20 | ✅ Complet |
Avec Clang 20, std::print est également complet. Attention toutefois : par défaut sur Ubuntu, Clang utilise libstdc++ (la librairie standard de GCC), pas libc++. Le support de <print> dépend alors de la version de libstdc++ installée, pas de celle de libc++.
Pour utiliser Clang avec libc++ :
clang++ -std=c++23 -stdlib=libc++ hello_print.cpp -o hello_printPour utiliser Clang avec libstdc++ (le défaut sur Ubuntu) :
clang++ -std=c++23 hello_print.cpp -o hello_printDans les deux cas, avec les versions 2026 de ces librairies, <print> est disponible.
Sur Ubuntu, l'installation par défaut de Clang utilise la libstdc++ fournie par GCC. C'est une combinaison qui fonctionne bien en pratique, mais le support des fonctionnalités de la librairie standard dépend de la version de libstdc++, pas de Clang. Concrètement :
# Vérifier la version de libstdc++ installée
dpkg -l | grep libstdc++
# Vérifier la version de GCC dont elle provient
g++ --versionSi votre système a libstdc++ 14 ou supérieure (venant de GCC 14+), <print> est disponible quelle que soit la version de Clang.
Avant de s'engager dans une stratégie de repli, il faut d'abord vérifier ce qui fonctionne. Voici trois méthodes de vérification, de la plus simple à la plus robuste.
La vérification la plus directe est de compiler un programme minimal :
// test_print.cpp
#include <print>
int main() {
std::println("std::print fonctionne !");
std::println("Entier : {}", 42);
std::println("Flottant : {:.2f}", 3.14159);
std::println("Chaîne : {}", "hello");
return 0;
}g++ -std=c++23 test_print.cpp -o test_print && ./test_printSi la compilation réussit et l'exécution produit la sortie attendue, votre environnement est prêt. Si la compilation échoue, le message d'erreur indique le problème.
L'erreur fatal error: print: No such file or directory signifie que votre librairie standard ne fournit pas <print>. C'est le cas avec libstdc++ antérieure à la version 14 ou libc++ antérieure à la version 19.
L'erreur error: 'println' is not a member of 'std' avec un #include <print> réussi signifie que le header existe mais que l'implémentation est incomplète ou que le standard activé n'est pas suffisant.
La méthode la plus fiable pour un code portable est de tester la feature test macro __cpp_lib_print :
// test_feature.cpp
#include <version>
#include <iostream>
int main() {
#ifdef __cpp_lib_print
std::cout << "__cpp_lib_print = " << __cpp_lib_print << std::endl;
std::cout << "std::print est disponible !" << std::endl;
#else
std::cout << "std::print n'est PAS disponible." << std::endl;
#endif
#ifdef __cpp_lib_format
std::cout << "__cpp_lib_format = " << __cpp_lib_format << std::endl;
std::cout << "std::format est disponible !" << std::endl;
#else
std::cout << "std::format n'est PAS disponible." << std::endl;
#endif
return 0;
}g++ -std=c++23 test_feature.cpp -o test_feature && ./test_feature__cpp_lib_print = 202211
std::print est disponible !
__cpp_lib_format = 202304
std::format est disponible !
Les valeurs attendues sont __cpp_lib_print >= 202207L et __cpp_lib_format >= 202110L (les valeurs exactes dépendent de la version du compilateur et de l'évolution du standard). Si la macro n'est pas définie, le composant n'est pas disponible.
Sans même écrire un fichier source :
# Vérifier __cpp_lib_print
echo '#include <version>' | g++ -std=c++23 -dM -E -x c++ - 2>/dev/null | grep __cpp_lib_print
# Vérifier __cpp_lib_format
echo '#include <version>' | g++ -std=c++23 -dM -E -x c++ - 2>/dev/null | grep __cpp_lib_format
# Lister toutes les feature test macros de la librairie
echo '#include <version>' | g++ -std=c++23 -dM -E -x c++ - 2>/dev/null | grep __cpp_lib | sortSi la commande grep ne produit aucune sortie, la macro n'est pas définie et le composant n'est pas supporté.
Sur Ubuntu, la version de GCC installée par défaut dépend de la version de la distribution. Voici l'état pour les versions LTS les plus courantes :
| Ubuntu | GCC par défaut | libstdc++ | <format> |
<print> |
|---|---|---|---|---|
| 20.04 LTS | GCC 9 | libstdc++ 9 | ❌ | ❌ |
| 22.04 LTS | GCC 11 | libstdc++ 11 | ❌ | ❌ |
| 24.04 LTS | GCC 14 | libstdc++ 14 | ✅ | ✅ |
| 24.10+ | GCC 14–15 | libstdc++ 14–15 | ✅ | ✅ |
Sur Ubuntu 22.04 LTS (encore très répandue dans les environnements de production et de CI), le GCC par défaut ne supporte ni <format> ni <print>. Deux solutions existent.
La première est d'installer un GCC récent via le PPA ubuntu-toolchain-r :
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install g++-14 Puis compiler avec :
g++-14 -std=c++23 hello_print.cpp -o hello_printLa seconde est d'utiliser un conteneur Docker avec une distribution récente pour la compilation (voir section 37) — c'est l'approche la plus propre en CI/CD.
📎 La section 2.1.1 couvre la gestion des versions de compilateurs avec
update-alternatives, et la section 2.4.3 traite des DevContainers pour des environnements de compilation reproductibles.
Si votre environnement ne supporte pas <print>, plusieurs alternatives existent. Elles sont classées ici de la plus recommandée à la moins souhaitable.
La librairie {fmt} est l'ancêtre de std::format et std::print. Son API est quasi identique, et elle fonctionne avec des compilateurs bien plus anciens (GCC 7+, Clang 5+).
Installation via le gestionnaire de paquets (Ubuntu 22.04+) :
sudo apt install libfmt-devOu via Conan / vcpkg pour un contrôle de version plus précis (voir section 27).
Utilisation :
#include <fmt/core.h>
int main() {
fmt::println("Bienvenue sur {} !", "Ubuntu");
fmt::println("Pi = {:.4f}", 3.14159);
return 0;
}g++ -std=c++17 hello_fmt.cpp -o hello_fmt -lfmtL'option -lfmt lie la librairie. Notez que {fmt} ne nécessite que C++17 — un avantage considérable si vous êtes bloqué sur un compilateur ancien.
La migration de {fmt} vers std::print est triviale :
// Avant (fmt)
#include <fmt/core.h>
fmt::println("x={}", 42);
// Après (standard)
#include <print>
std::println("x={}", 42);Un changement de header et de namespace suffit dans la majorité des cas. Pour les projets qui doivent supporter les deux, un header de compatibilité peut automatiser la bascule :
// compat_print.hpp
#pragma once
#ifdef __cpp_lib_print
#include <print>
namespace io = std;
#else
#include <fmt/core.h>
namespace io = fmt;
#endifUtilisation :
#include "compat_print.hpp"
int main() {
io::println("Fonctionne partout : {}", 42);
return 0;
}Ce pattern permet d'écrire du code avec la syntaxe std::print dès aujourd'hui, tout en supportant les environnements qui n'ont pas encore <print>. Quand le support sera universel, il suffira de supprimer le header de compatibilité et de remplacer io:: par std::.
Si <format> est disponible (GCC 13+, Clang 17+) mais pas <print>, vous pouvez combiner std::format avec std::cout :
#include <format>
#include <iostream>
int main() {
std::cout << std::format("Bienvenue sur {} !\n", "Ubuntu");
std::cout << std::format("Pi = {:.4f}\n", 3.14159);
return 0;
}Vous bénéficiez de la syntaxe de chaîne de format et de la sécurité de type, mais vous perdez les avantages de performance de std::print (puisque std::format construit une std::string temporaire qui est ensuite passée à std::cout).
C'est une solution de transition acceptable pour les projets sur GCC 13 qui ne peuvent pas installer {fmt}.
En dernier recours, sur des compilateurs anciens sans <format>, il reste std::cout. C'est l'approche la plus portable mais la moins satisfaisante :
#include <iostream>
#include <iomanip>
int main() {
std::cout << "Bienvenue sur Ubuntu !" << std::endl;
std::cout << "Pi = " << std::fixed << std::setprecision(4)
<< 3.14159 << std::endl;
return 0;
}Si vous êtes dans cette situation, l'adoption de {fmt} est fortement recommandée plutôt que de rester avec std::cout.
Dans un projet CMake, on peut détecter le support au moment de la configuration et choisir automatiquement la meilleure option disponible :
include(CheckIncludeFileCXX)
# Vérifier le support de <print>
set(CMAKE_REQUIRED_FLAGS "-std=c++23")
check_include_file_cxx("print" HAS_STD_PRINT)
# Vérifier le support de <format>
check_include_file_cxx("format" HAS_STD_FORMAT)
if(HAS_STD_PRINT)
message(STATUS "std::print disponible — utilisation du standard")
target_compile_definitions(mon_projet PRIVATE USE_STD_PRINT)
elseif(HAS_STD_FORMAT)
message(STATUS "std::format disponible — repli sur format + cout")
target_compile_definitions(mon_projet PRIVATE USE_STD_FORMAT)
else()
message(STATUS "Repli sur {fmt}")
find_package(fmt REQUIRED)
target_link_libraries(mon_projet PRIVATE fmt::fmt)
target_compile_definitions(mon_projet PRIVATE USE_FMT)
endif()Côté code :
// print_compat.hpp
#pragma once
#if defined(USE_STD_PRINT)
#include <print>
namespace io = std;
#elif defined(USE_FMT)
#include <fmt/core.h>
namespace io = fmt;
#elif defined(USE_STD_FORMAT)
#include <format>
#include <iostream>
// Fallback : pas de io::println, mais io::format disponible
namespace io {
using std::format;
template<typename... Args>
void println(std::format_string<Args...> fmt, Args&&... args) {
std::cout << std::format(fmt, std::forward<Args>(args)...) << '\n';
}
}
#endifCette approche permet de maintenir un code unique avec la syntaxe io::println(...), quelle que soit la librairie sous-jacente.
💡 Cet extrait CMake est un aperçu simplifié. La section 26 couvre CMake en profondeur, et la section 27 traite la gestion des dépendances externes comme
{fmt}.
La formation cible Ubuntu avec GCC 15 (ou Clang 20), où std::print est pleinement supporté. Tous les exemples de code utilisent directement <print> et -std=c++23 sans couche de compatibilité.
Si vous travaillez sur un environnement différent, voici le résumé des options par ordre de préférence :
| Votre environnement | Action recommandée |
|---|---|
| GCC 14+ / Clang 19+ | Utiliser std::print directement |
| GCC 13 / Clang 17-18 | Installer {fmt}, ou utiliser std::format + cout |
| GCC 11-12 | Installer {fmt} (seule option viable) |
| GCC < 11 | Mettre à jour le compilateur (priorité absolue) |
La librairie {fmt} est la solution de repli universelle. Elle est légère, mature, et son API est suffisamment proche du standard pour que la migration soit quasi transparente le jour où vous passerez à std::print.
En mars 2026, std::print est pleinement supporté par GCC 14+ (libstdc++ 14+) et Clang 19+ (libc++ 19+). Sur Ubuntu 24.04 LTS et versions ultérieures, tout fonctionne sans configuration particulière. Sur des versions plus anciennes, l'installation d'un compilateur récent ou l'utilisation de {fmt} comme solution de repli permet d'écrire du code avec la même syntaxe.
La feature test macro __cpp_lib_print est le mécanisme le plus fiable pour détecter le support à la compilation. Le header de compatibilité avec un alias de namespace (namespace io = std; ou namespace io = fmt;) permet d'écrire du code portable entre les deux implémentations.
Le paysage du support évolue rapidement. Chaque nouvelle version de GCC et Clang étend la couverture de C++23 et C++26. En cas de doute, la commande echo '#include <version>' | g++ -std=c++23 -dM -E -x c++ - | grep __cpp_lib vous donne une réponse immédiate.
Fin du Module 2 — Vous disposez maintenant d'une toolchain complète sur Ubuntu : compilateurs installés et configurés (2.1-2.2), cache de compilation en place (2.3), IDE prêt (2.4), compréhension de la chaîne de compilation et des outils d'inspection (2.5), maîtrise des options de compilation critiques (2.6), et connaissance du système d'affichage moderne (2.7). Le Module 3 aborde les fondamentaux du langage C++ à proprement parler, en commençant par les types, variables et opérateurs.