40.1 C++23 laborprofil, szabványstátusz és feature-test

A C++23 laborprofil kiterjesztése nem pusztán egy újabb -std= kapcsoló beírása a buildparancsba. Azt kell rögzíteni, milyen bizonyítékot fogadunk el arra, hogy egy nyelvi vagy könyvtári képesség tényleg használható a kiválasztott toolchainben. A C++20-as alapokat itt nem ismételjük; a feature-test és build-capability alapokhoz lásd Feature-test, standard library és build capability (33.5), a szabvány- és dialektusszerződéshez Szabványok, dialektusok és hordozhatósági szerződés (1.2), a reprodukálható laborhoz Reprodukálható fordítási és diagnosztikai labor (1.3). Ez az alfejezet a C++23-ra való átállás státusz- és ellenőrzési kérdéseit tárgyalja.

40.1.1 A C++23 állapot három rétege

A C++23-cal kapcsolatban három különböző dolgot kell megkülönböztetni.

Az ISO/IEC 14882:2024 a kiadott, normatív szabvány. Ez mondja meg, mit jelent a C++23 nyelv és a standard könyvtár a szabvány szövege szerint. A N4950 nyilvános munkatervezet: hasznos, hozzáférhető dokumentum a szabvány tartalmának megértéséhez, de nem azonos a kiadott ISO szöveggel. A munkatervezet és a kiadott szabvány között szerkesztői és szavazási folyamatból eredő eltérések lehetnek; egy fordító pedig jellemzően nem „a teljes C++23-at” implementálja, hanem egyes javaslatokat, azok adott állapotában.

A harmadik réteg maga az implementáció. Ide tartozik a fordító, a standard könyvtár, a linker és a futásidejű környezet. Egy képesség lehet a szabványban, szerepelhet a munkatervezetben, és mégis hiányozhat a laborban használt GCC/Clang vagy libstdc++/libc++ párosból. Ezért a laborprofilban nem elég azt írni, hogy „C++23”; rögzíteni kell a konkrét fordítót, a targetet, a standard könyvtár implementációját és verzióját, valamint a vizsgált feature-test makrókat.

Réteg Kérdés Tipikus bizonyíték Korlát
Kiadott szabvány Mi normatív? ISO/IEC 14882:2024 Nem mondja meg, melyik toolchain támogatja
Munkatervezet Mi volt a tervezet tartalma? N4950 Nem azonos a kiadott szöveggel
Implementáció Mit tud a labor? feature-test, compile/link/run probe Verzió-, target- és kapcsolófüggő

A konkrét GCC/Clang/libstdc++/libc++ támogatási táblázatot ebben az anyagban nem tekintjük ellenőrzöttnek. A helyes minta: a laborprofil rögzíti a tényleges verziókat, a fejezet pedig olyan ellenőrzési eljárást ad, amely ezekhez a verziókhoz köthető.

40.1.2 Fordítási mód és a __cplusplus jelentése

A C++23-as profil alapesetben explicit módon kéri a nyelvi módot:

g++ -std=c++23 -Wall -Wextra -Wpedantic -O2 probe.cpp -o probe-gcc
clang++ -std=c++23 -Wall -Wextra -Wpedantic -O2 probe.cpp -o probe-clang

A -std=c++23 a célzott név. Egyes fordítókban átmeneti névként -std=c++2b is elfogadott lehet, de ez verziófüggő, és a laborban nem helyettesítheti a ténylegesen támogatott kapcsoló ellenőrzését. A -std=c++23 elfogadása önmagában még nem bizonyítja, hogy minden C++23-as képesség elérhető.

A C++23-as nyelvi módban a szabvány szerinti elvárt érték:

static_assert(__cplusplus == 202302L, "nem C++23 nyelvi mód");

Ez az érték a fordítótól és a nyelvi módtól függ; a laborban ellenőrizni kell, hogy a fordító tényleg ezt definiálja-e. A __cplusplus == 202302L azt mutatja, hogy a fordítás nyelvi módja C++23-ként van beállítva. Nem bizonyítja, hogy a standard könyvtár adott headerjei elérhetők, hogy egy adott túlterhelés létezik, vagy hogy a linker megtalálja a szükséges szimbólumot.

Érdemes külön kezelni a következőket:

40.1.3 Feature-test makrók: nyelvi és könyvtári képesség

A feature-test makró olyan előre definiált makró, amelynek neve és értéke egy képesség jelenlétét és változatát jelzi. A nyelvi képességeknél jellemzően __cpp_*, a könyvtári képességeknél __cpp_lib_* alakot látsz. A könyvtári makrók jelentős része a <version> headerből érhető el, ezért a könyvtári ellenőrzés előtt ezt be kell include-olni.

Fontos feltétel: a makró létezése implementációs állítás, nem teljes körű garancia. Ellenőrizni kell a nevet és a küszöbértéket is, lehetőleg #if defined(...) && ... >= küszöb formában. A puszta #ifdef egy korábbi, részleges vagy kísérleti értéket is elfogadhat.

A gyakorlati ellenőrzés három szintje:

  1. Fordítási próba: a fordító elfogadja-e a szintaxist és a headert.
  2. Linkelési próba: a szükséges szimbólum megtalálható-e, nem kell-e külön kapcsoló.
  3. Futásidejű füstteszt: a képesség a várt alapviselkedést mutatja-e.

A feature-test makró ebből az első kettőt nem feltétlenül fedi le. Ezért a laborprofilban a makró mellett compile probe, link probe és rövid runtime smoke test szerepeljen.

Levezetés: miért nem elég a __cplusplus?

  1. A __cplusplus értékét a fordító a nyelvi mód alapján állítja be.
  2. A standard könyvtár headerjeit és implementációját ettől függetlenül választja a toolchain.
  3. Egy nyelvi feature-test makró a fordító, egy __cpp_lib_* makró a könyvtár állítását tükrözi.
  4. Ezért a nyelvi mód, a nyelvi képesség és a könyvtári képesség külön ellenőrizendő; a következtetés csak a három együttes eredményéből vonható le.

Az alábbi példa egy nyelvi és egy könyvtári képességre ad capability checket, fallbackkal. A küszöbértékek szemléltetők; a laborban a tényleges <version> és fordítási dokumentáció alapján kell megerősíteni őket.

#include <version>
#include <cstdio>

#if defined(__cpp_explicit_this_parameter) && __cpp_explicit_this_parameter >= 202110L
#  define HAVE_EXPLICIT_THIS 1
#else
#  define HAVE_EXPLICIT_THIS 0
#endif

#if defined(__cpp_lib_expected) && __cpp_lib_expected >= 202202L
#  include <expected>
#  define HAVE_EXPECTED 1
#else
#  define HAVE_EXPECTED 0
#endif

struct Probe {
    int value = 42;

#if HAVE_EXPLICIT_THIS
    void ping(this Probe& self) {
        std::printf("explicit-this=1 expected=%d value=%d\n",
                    HAVE_EXPECTED, self.value);
    }
#else
    void ping() {
        std::printf("explicit-this=0 expected=%d value=%d\n",
                    HAVE_EXPECTED, value);
    }
#endif
};

int main() {
    Probe p;
    p.ping();
}

A példa nem állítja, hogy bármely konkrét fordítón teszteltük. A várt ágak: C++20 módban jellemzően a fallback ág fut; teljes C++23 támogatásnál az explicit object parameter ág; részleges könyvtári támogatásnál a nyelvi ág igen, a HAVE_EXPECTED érték viszont 0 lehet. A tényleges kimenetet a labor rögzíti.

40.1.4 Támogatási állapot: fordító, könyvtár és linkelés

A GCC és a Clang nyelvi támogatása, valamint a libstdc++ és a libc++ könyvtári támogatása külön tengely. A GCC jellemzően libstdc++-szal dolgozik; a Clang választhat libstdc++ és libc++ között, például -stdlib=libc++ kapcsolóval. A laborprofilnak rögzítenie kell a tényleges párosítást, mert a „Clang támogatja” állítás önmagában félrevezető: a kérdés az, hogy a Clang melyik standard könyvtárral, melyik targeten, milyen kapcsolókkal.

Részleges támogatásnál előfordulhat, hogy a nyelvi szintaxis fordul, a header létezik, de a feature-test makró hiányzik; vagy a makró definiált, de a implementáció hiányos. Kísérleti könyvtári elemek std::experimental névtérben vagy külön headerben lehetnek, és nem részei a C++23 standardnak. Egyes GCC/libstdc++ kiadásokban bizonyos C++23-as könyvtári elemek külön linkelési kapcsolót igényelhetnek, például -lstdc++exp; ezt nem szabad találgatni, hanem a labour dokumentálni kell. A linkelési igényt a linker hibája vagy a könyvtár dokumentációja igazolhatja.

Rögzítendő adat Példa Miért kell
Fordító és verzió GCC x.y vagy Clang x.y A nyelvi támogatás verziófüggő
Target x86-64 System V, LP64 ABI és elérhetőség targetfüggő
Standard flag -std=c++23, extensions off A nyelvi mód pontos azonosítása
Standard könyvtár libstdc++ x.y vagy libc++ x.y A könyvtári támogatás külön tengely
Feature-test snapshot __cpp_*, __cpp_lib_* értékek A tényleges állítás rögzítése
Linkelési kapcsolók pl. -lstdc++exp, ha dokumentált A linkelési próba feltétele
Dátum a mérés/ellenőrzés dátuma A támogatási állapot változhat

40.1.5 Kétprofilos CMake és CI mátrix

A C++20 profil maradjon meg baseline-ként, a C++23 pedig jelölt profilként fusson. A kettő együtt mutatja meg, hogy a fallback valóban működik-e, és hogy a C++23-as ág nem csak a fejlesztői gépen fordul-e le. Egy minimális, kétprofilos CMake-példa:

cmake_minimum_required(VERSION 3.20)
project(cpp23_probe LANGUAGES CXX)

set(PROBE_CXX_STANDARD "23" CACHE STRING "20 vagy 23")
set(CMAKE_CXX_STANDARD "${PROBE_CXX_STANDARD}")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_executable(probe probe.cpp)
target_compile_options(probe PRIVATE -Wall -Wextra -Wpedantic)

enable_testing()
add_test(NAME probe_runs COMMAND probe)

A labor CMake-verziójában ellenőrizni kell, hogy a 23 érték ismert-e; ha nem, a standard flaget target-szinten kell átadni, és ezt a profilban dokumentálni kell. A két profil buildje:

cmake -S . -B build20 -DPROBE_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=Debug
cmake --build build20
ctest --test-dir build20 --output-on-failure

cmake -S . -B build23 -DPROBE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Debug
cmake --build build23
ctest --test-dir build23 --output-on-failure

A CI mátrixban érdemes legalább a „GCC + libstdc++” és a „Clang + a laborban rögzített standard könyvtár” párost futtatni, mindkettőt C++20 és C++23 módban. A C++20 futás a fallbacket ellenőrzi; a C++23 futás a jelölt képességeket. Ha a C++23-as build elbukik, nem szabad csendben C++20-ra váltani: a képességet hiányzóként kell jelölni, a fallbacknek pedig továbbra is működnie kell.

40.1.6 Kidolgozott példa: a probe értelmezése

A fenti probe.cpp három tipikus profilon a következőképpen viselkedhet. Az értékek illusztratívak, nem mért eredmények.

Profil __cplusplus __cpp_explicit_this_parameter __cpp_lib_expected Várható ág
C++20 baseline 202002L nincs definiálva nincs definiálva fallback mindkettőre
C++23 nyelvi, részleges könyvtár 202302L definiált, megfelelő küszöb nincs definiálva explicit this, expected fallback
C++23 teljes laborprofil 202302L definiált definiált explicit this és expected elérhető

A diagnózis menete: először a __cplusplus és a nyelvi feature-test makró mondja meg, hogy a fordító mit állít. Utána a <version> és a __cpp_lib_* makró a könyvtár állítását mutatja. Végül a linkelési és futásidejű próba igazolja, hogy az állítás a laborban használható. Ha a makró definiált, de a linkelés elbukik, a profil hiányos; ha a makró nincs definiálva, a fallback ágat kell használni, és a hiányt dokumentálni kell.

40.1.7 Gyakori hibák

40.1.8 Összefoglaló

A C++23 laborprofil háromrétegű: kiadott szabvány, nyilvános munkatervezet és tényleges implementáció. A -std=c++23 és a __cplusplus == 202302L a nyelvi módot azonosítja, de nem bizonyítja a könyvtári képességeket. A nyelvi __cpp_* és könyvtári __cpp_lib_* feature-test makrókat a <version> headerrel együtt kell ellenőrizni, lehetőleg küszöbértékkel. A támogatási állapotot compiler, standard könyvtár, target, linkelési kapcsolók és dátum szerint kell rögzíteni. A kétprofilos C++20/C++23 CMake és CI mátrix biztosítja, hogy a fallback is működjön, és a C++23-as ág ne csendben, ellenőrizetlenül kerüljön be a buildbe.

40.1.9 Feladatok

  1. Státuszdiagnózis. Egy laborban a __cplusplus értéke 202302L, a <version> include-olható, de a __cpp_lib_expected nem definiált. Mit jelent ez, és milyen további ellenőrzéseket végzel? Írj rövid döntési fát.

  2. Feature-test és fallback. Egészítsd ki a fejezet Probe példáját egy static operator()-t használó képességellenőrzéssel úgy, hogy C++20-ban is forduljon, és a fallback viselkedése megmaradjon. Add meg a buildparancsot is.

  3. Kétprofilos CMake. Készíts olyan CMake-beállítást, amely ugyanazt a forrást C++20 és C++23 módban is lefordítja, és mindkét profilban futtat egy füsttesztet. Fogalmazd meg, mit jelent a CI-ben, ha a C++23-as profil elbukik.

  4. Linkelési hiba. A C++23-as build a nyelvi szintaxist elfogadja, de a linker egy standard könyvtári szimbólumra hivatkozó hibát ad. Milyen sorrendben vizsgálódsz, és milyen bizonyítékot rögzítesz a laborprofilban?

40.1.9.1 Megoldások

1. Megoldás. A __cplusplus == 202302L azt valószínűsíti, hogy a nyelvi mód C++23. A hiányzó __cpp_lib_expected azt jelzi, hogy a vizsgált standard könyvtár nem állítja a std::expected adott változatának elérhetőségét. Ez nem feltétlenül azt jelenti, hogy a header egyáltalán nem létezik; lehet, hogy a makró hiányzik, a header kísérleti, vagy a könyvtár verziója eltér. Ellenőrzési sorrend: <version> include; __cpp_lib_expected és küszöb; <expected> include próba; egyszerű std::expected használat fordítása; linkelés; futásidejű smoke test. Ha bármelyik szint hiányzik, a std::expected-et használó kódot fallback mögé kell tenni, és a laborprofilban „részleges” állapotként kell rögzíteni. Ellenőrzési feltétel: a fallback C++20-ban és a C++23-as részleges profilban is fordul és fut.

2. Megoldás. Egy lehetséges kiegészítés:

#include <version>
#include <cstdio>

struct Functor {
#if defined(__cpp_static_call_operator) && __cpp_static_call_operator >= 202207L
    static int operator()(int x) { return x + 1; }
#else
    int operator()(int x) const { return x + 1; }
#endif
};

int main() {
    std::printf("%d\n", Functor{}(41));
}

A C++23-as ágban a static operator() elérhető, a fallback ágban nem statikus tagfüggvény marad. A hívás Functor{}(41) mindkét ágban működik. Build:

g++ -std=c++20 -Wall -Wextra -Wpedantic static_call.cpp -o static_call20
g++ -std=c++23 -Wall -Wextra -Wpedantic static_call.cpp -o static_call23

Indoklás: a feature-test makró a fordító állítását ellenőrzi; a fallback biztosítja, hogy a C++20 baseline ne törjön el. Ellenőrzési feltétel: mindkét build sikeres, és a futtatás 42-t ad. A küszöbértéket a laborban a tényleges <version> alapján kell megerősíteni.

3. Megoldás. A CMake-példa már mutatja a PROBE_CXX_STANDARD cache változót és a ctest futtatást. A CI-ben két külön build és teszt fut:

cmake -S . -B build20 -DPROBE_CXX_STANDARD=20
cmake --build build20
ctest --test-dir build20 --output-on-failure

cmake -S . -B build23 -DPROBE_CXX_STANDARD=23
cmake --build build23
ctest --test-dir build23 --output-on-failure

Ha a C++23-as profil elbukik, a helyes következtetés nem az, hogy „a C++23 nem támogatott általában”, hanem hogy a rögzített compiler/stdlib/target kombinációban a vizsgált képesség nem használható. A C++20 baseline-nak továbbra is zöldnek kell lennie; a C++23-as bukást a laborprofilban a konkrét hibaüzenettel, a feature-test állapottal és a dátummal kell rögzíteni. Ellenőrzési feltétel: a C++20 build és teszt sikeres, a C++23 hiba pedig reprodukálható és dokumentált.

4. Megoldás. Először a linkerüzenetet kell elolvasni: hiányzó szimbólum, hiányzó könyvtár vagy nem megfelelő könyvtárverzió szerepel-e benne. Ezután ellenőrizni kell a compiler és a standard könyvtár párosítását, a könyvtár dokumentált linkelési igényét, például a -lstdc++exp szükségességét, majd a buildparancsot. Ha a kapcsoló dokumentált és elérhető, a linkelési próbát meg kell ismételni; ha nem, a képességet fallback mögé kell tenni. A laborprofilban rögzíteni kell a fordító verzióját, a standard könyvtár verzióját, a targetet, a linkelési kapcsolót, a linker hibaüzenetét és a fallback működését. Ellenőrzési feltétel: a hiba vagy dokumentált kapcsolóval megszűnik, vagy a fallback build és futás igazoltan működik.