SFINAE ( Eșecul de substituție în engleză nu este o eroare , „substituția eșuată nu este o eroare”) este o regulă de limbaj C ++ asociată cu șabloanele și supraîncărcarea funcțiilor . Este utilizat pe scară largă „în alte scopuri” - pentru reflecție în timpul compilării : în funcție de proprietățile tipului, compilarea merge într-un fel sau altul.
Regula SFINAE spune: Dacă tipurile/valorile finale ale parametrilor șablonului unei funcții nu pot fi calculate, compilatorul nu aruncă o eroare, ci caută o altă supraîncărcare adecvată. Eroarea va fi în trei cazuri:
Regula exista în C++98 și a fost inventată astfel încât programul să nu genereze erori dacă undeva în fișierele antet ar exista un șablon cu același nume, departe de context. Dar mai târziu s-a dovedit a fi convenabil pentru reflecție în timpul compilării. Acronimul SFINAE a fost inventat de David Vandervoord, autorul cărții C++ Patterns (2002).
La Boost a fost adăugat un șablon simplu care funcționează pe regula SFINAE și vă permite să instanțiați un șablon în anumite condiții. enable_if
În standardul C++11 , regula SFINAE a fost oarecum rafinată fără a schimba conceptul. A fost introdus și șablonul acolo (în general , , , și multe altele au fost împrumutate de la Boost ).enable_ifchronorandomfilesystem
În C++17 , a fost adăugat un construct care a redus ușor nevoia de SFINAE. if constexpr()
C ++20 a introdus . Pe de o parte, constanta dintre paranteze este, de asemenea, parte a substituției, iar dacă nu reușește să calculeze, va fi o substituție eșuată. Pe de altă parte, reduce și nevoia de SFINAE. De asemenea, a redus necesitatea conceptului SFINAE . explicit (true)
Să presupunem că trebuie să apelăm o funcție
f ( 1 , 2 );Există versiuni ale acestei funcții:
( 1 ) void f ( int , std :: vector < int > ); ( 2 ) void f ( int , int ); ( 3 ) void f ( dublu , dublu ); ( 4 ) void f ( int , int , char , std :: string , std :: vector < int > ); ( 5 ) void f ( std :: șir ); ( 6 ) nul f (...);Compilatorul colectează aceste funcții într-o listă și o găsește pe cea mai bună conform anumitor reguli - produce rezoluție de supraîncărcare .
Pasul 2, legat de funcțiile șablonului, nu a fost încă activat. Să adăugăm încă două funcții la lista noastră.
( 7 ) template < typename T > gol f ( T , T ); ( 8 ) template < typename T > void f ( T , typename T :: iterator );Funcția 7 va fi eliminată în al patrulea pas, deoarece o funcție care nu este șablon este întotdeauna „mai puternică” decât una șablon.
Șablonul 8 este departe de sarcina noastră, deoarece este conceput pentru o anumită clasă care are tipul iterator. Al doilea pas este SFINAE : compilatorul spune că T = int, încearcă să înlocuiască intîn șablon, iar acele șabloane în care înlocuirea nu a dus la succes sunt eliminate. Prin urmare, o înlocuire eșuată nu este o eroare .
Acest exemplu se compilează chiar și în C++03 .
#include <iostream> #include <vector> #include <set> template < typenameT > _ clasa DetectFind { struct Fallback { int găsi ; }; // adaugă numele de membru „find” struct Derivat : T , Fallback { }; template < typename U , U > struct Check ; typedef char Da [ 1 ]; // typedef pentru o matrice de dimensiunea unu. typedef char Nr [ 2 ]; // typedef pentru o matrice de dimensiunea doi. șablon < nume tip U > static No & func ( Verificare < int Fallback ::* , & U :: find > * ); șablon < nume tip U > static Da & func (...); public : typedef DetectFind tip ; enum { value = sizeof ( func < Derived > ( 0 )) == sizeof ( Da ) }; }; int main () { std :: cout << DetectFind < std :: vector < int >> :: value << ' ' << DetectFind < std :: set < int > >:: value << std :: endl ; returnează 0 ; }Cum funcționează: rezoluția supraîncărcării are loc într-un șir , iar tipul concret este mai puternic decât argumentele variabile . Datorită faptului că sub , nu este nevoie să instanțiați funcții șablon, este suficient să înlocuiți tipuri - prin urmare, funcțiile au doar anteturi fără corpuri. A doua funcție, care returnează tipul , va fi întotdeauna înlocuită, dar cum rămâne cu prima? sizeof(func<Derived>(0))Check<int Fallback::*, &U::find> *...funcsizeofYes
Va fi înlocuit dacă tipul șablonului va exista (deoarece sub indicator, tipul exact nu este important, principalul lucru este existența). Primul parametru șablon este un tip, al doilea este o constantă de acel tip. Pointerul către câmpul obiectului este luat ca tip (de fapt, decalajul de la începutul obiectului către câmp), ca constantă, pointerul către câmp . Constanta va fi definită și de tipul corect dacă singurul câmp este luat din obiect - adică nu există niciun alt împrumutat de la . CheckCheckintFallbackfindDerived::findFallbackfindT
C++ | |
---|---|
Particularități | |
Unele biblioteci | |
Compilatoare | |
influențat | |
|