SFINAE ( englanninkielinen korvausvirhe ei ole virhe , "epäonnistunut korvaaminen ei ole virhe") on C++- kielisääntö, joka liittyy malleihin ja toimintojen ylikuormitukseen . Sitä käytetään laajalti "muihin tarkoituksiin" - pohdiskeluun kokoamisen aikana : tyypin ominaisuuksista riippuen kokoaminen menee tavalla tai toisella.
SFINAE-sääntö sanoo: Jos funktion malliparametrien lopullisia tyyppejä/arvoja ei voida laskea, kääntäjä ei anna virhettä, vaan etsii muuta sopivaa ylikuormitusta. Virhe ilmenee kolmessa tapauksessa:
Sääntö oli olemassa jo C++98 :ssa , ja se keksittiin, jotta ohjelma ei aiheuttaisi virheitä, jos jossain otsikkotiedostoissa on samanniminen mallipohja, kaukana kontekstista. Mutta myöhemmin se osoittautui käteväksi pohdittavaksi kokoamisen aikana. Lyhenteen SFINAE loi David Vandervoord, C++ Patterns (2002) -kirjan kirjoittaja.
Boostiin on lisätty yksinkertainen malli, joka toimii SFINAE -säännöllä ja mahdollistaa mallin luomisen tietyissä olosuhteissa. enable_if
C++11 - standardissa SFINAE-sääntöä on hiottu jonkin verran konseptia muuttamatta. Malli tuli myös sinne (yleensä , , , ja paljon muuta lainattiin Boostilta ).enable_ifchronorandomfilesystem
C++17 : ssä lisättiin konstruktio , joka vähensi hieman SFINAE:n tarvetta. if constexpr()
C ++20 esitteli . Toisaalta suluissa oleva vakio on myös osa substituutiota, ja jos se ei onnistu laskemaan, se on epäonnistunut substituutio. Toisaalta se myös vähentää SFINAE:n tarvetta. Vähensi myös SFINAE - konseptin tarvetta . explicit (true)
Oletetaan, että meidän on kutsuttava funktio
f ( 1,2 ) ; _Tästä toiminnosta on olemassa versioita:
( 1 ) void f ( int , std :: vektori < int > ); ( 2 ) void f ( int , int ); ( 3 ) void f ( double , double ); ( 4 ) void f ( int , int , char , std :: string , std :: vektori < int > ); ( 5 ) void f ( std :: merkkijono ); ( 6 ) mitätön f (...);Kääntäjä kokoaa nämä funktiot listaksi ja löytää niistä parhaan tiettyjen sääntöjen mukaan - se tuottaa ylikuormitusresoluutiota .
Vaihe 2, joka liittyy mallitoimintoihin, ei ole vielä aktivoitu. Lisätään vielä kaksi funktiota luetteloomme.
( 7 ) malli < typename T > void f ( T , T ); ( 8 ) malli < typename T > void f ( T , tyyppinimi T :: iteraattori );Funktio 7 hylätään neljännessä vaiheessa, koska ei-mallifunktio on aina "vahvempi" kuin mallipohjainen.
Malli 8 on kaukana tehtävästämme, koska se on suunniteltu tietylle luokalle, jolla on tyyppi iterator. Toinen vaihe on SFINAE : kääntäjä sanoo, että T = int, yrittää korvata intmallissa, ja ne mallit, joissa korvaaminen ei johtanut menestykseen, hylätään. Siksi epäonnistunut korvaaminen ei ole virhe .
Tämä esimerkki kääntää jopa C++03 :ssa .
#include <iostream> #sisällytä <vektori> #include <set> malli < typenameT > _ luokka DetectFind { struct Fallback { int löytää ; }; // lisää jäsenen nimi "etsi" -rakenne Johdettu : T , Vara { }; template < typename U , U > struct Tarkista ; typedef char Kyllä [ 1 ]; // typedef yhden kokoiselle joukolle. typedef char No [ 2 ]; // typedef taulukolle, jonka koko on kaksi. malli < tyyppinimi U > staattinen Ei & func ( Tarkista < int Vara ::* , & U :: etsi > * ); malli < tyyppinimi U > staattinen Kyllä & toimi (...); julkinen : typedef DetectFind type ; enum { arvo = koko ( funktio < Johdettu > ( 0 )) == koko ( Kyllä ) }; }; int main () { std :: cout << DetectFind < std :: vektori < int >> :: arvo << ' ' << DetectFind < std :: set < int > >:: arvo << std :: endl ; paluu 0 ; }Kuinka se toimii: ylikuormitusresoluutio esiintyy merkkijonossa , ja konkreettinen tyyppi on vahvempi kuin muuttujaargumentit . Koska alla ei tarvitse ilmentää mallifunktioita, riittää tyypit korvaamaan - siksi funktioissa on vain otsikot ilman runkoja. Toinen funktio, joka palauttaa tyypin , korvataan aina, mutta entä ensimmäinen? sizeof(func<Derived>(0))Check<int Fallback::*, &U::find> *...funcsizeofYes
Se korvataan, jos mallityyppi on olemassa (koska osoittimen alla tarkka tyyppi ei ole tärkeä, pääasia on olemassaolo). Ensimmäinen malliparametri on tyyppi, toinen on kyseisen tyypin vakio. Osoitin objektin -kenttään otetaan tyypiksi (itse asiassa siirtymä kohteen alusta kenttään), vakiona osoitin kenttään . Vakio on määritelty ja oikean tyyppinen, jos ainoa kenttä on otettu objektista - eli ei ole muuta lainattua kohteesta . CheckCheckintFallbackfindDerived::findFallbackfindT
C++ | |
---|---|
Erikoisuudet | |
Jotkut kirjastot | |
Kääntäjät | |
vaikutti | |
|