SFINAE

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:

Historia

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)

Alkuperäinen tapaaminen

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 . 

  1. Ensinnäkin kääntäjä hylkää funktiot, jotka eivät vastaa parametrien määrää - 4 ja 5.
  2. Sitten mallien korvaukset hylätään, kun syöttöparametrien tyyppejä ja palautusta ei voitu laskea - niitä ei ole.
  3. Sitten toiminto 1 hylätään - sille ei ole sopivaa tyyppimuunnosa.
  4. Ja 2, 3 ja 6, melko monimutkaisten sääntöjen mukaan, kääntäjä valitsee 2 - molemmat tyypit ovat täsmälleen samat. Jos tällaista ehdotonta voittajaa ei olisi, kääntäjä antaisi virheilmoituksen, joka ilmoittaa, minkä vaihtoehtojen välillä se vaihtelee.

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 .

Esimerkki heijastuksesta SFINAE:llä käännettäessä

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

Muistiinpanot

Linkit

  • SFINAE  (englanniksi) . cppreference.com. Haettu 9. tammikuuta 2020. Arkistoitu alkuperäisestä 6. toukokuuta 2021.
Venäjäksi