SFINAE

SFINAE ( anglicky  selhání substituce není chyba , „neúspěšná substituce není chyba“) je pravidlo jazyka C++ spojené se šablonami a přetížením funkcí . Je široce používán „pro jiné účely“ - pro reflexi během kompilace : v závislosti na vlastnostech typu probíhá kompilace tak či onak.

Pravidlo SFINAE říká: Pokud nelze vypočítat konečné typy/hodnoty parametrů šablony funkce, kompilátor nevyhodí chybu, ale hledá jiné vhodné přetížení. Chyba bude ve třech případech:

Historie

Pravidlo existovalo již v C++98 a bylo vynalezeno, aby program negeneroval chyby, pokud by někde v hlavičkových souborech byla šablona se stejným názvem, daleko od kontextu. Ale později se ukázalo, že je to vhodné pro reflexi během kompilace. Zkratku SFINAE vymyslel David Vandervoord, autor C++ Patterns (2002).

Do Boost byla přidána jednoduchá šablona, ​​která funguje na pravidle SFINAE a umožňuje za určitých podmínek vytvořit instanci šablony. enable_if

Ve standardu C++11 bylo pravidlo SFINAE poněkud vylepšeno, aniž by se změnil koncept. Šablona tam také vstoupila (obecně , , a mnohem více bylo vypůjčeno z Boost ).enable_ifchronorandomfilesystem

V C++17 byl přidán konstrukt , který mírně snížil potřebu SFINAE. if constexpr()

C ++20 představilo . Jednak konstanta v závorkách je také součástí substituce, a pokud se nepodaří vypočítat, bude to neúspěšná substituce. Na druhou stranu to také snižuje potřebu SFINAE. Také se snížila potřeba konceptu SFINAE . explicit (true)

Původní schůzka

Předpokládejme, že potřebujeme zavolat funkci

f ( 1,2 ) ; _

Existují verze této funkce:

( 1 ) void f ( int , std :: vektor < int > ); ( 2 ) void f ( int , int ); ( 3 ) void f ( double , double ); ( 4 ) void f ( int , int , char , std :: string , std :: vector < int > ); ( 5 ) void f ( std :: string ); ( 6 ) neplatné f (...);

Kompilátor tyto funkce shromáždí do seznamu a podle určitých pravidel najde tu nejlepší – vytvoří rozlišení přetížení . 

  1. Nejprve kompilátor zahodí funkce, které neodpovídají počtu parametrů – 4 a 5.
  2. Poté jsou vyřazeny substituce šablon, kde nebylo možné vypočítat typy vstupních parametrů a návrat - žádné nejsou.
  3. Poté je funkce 1 zahozena - není pro ni vhodná konverze typu.
  4. A z 2, 3 a 6 si podle dost komplikovaných pravidel překladač vybere 2 - oba typy jsou úplně stejné. Pokud by takový absolutní vítěz neexistoval, kompilátor by hodil chybu udávající, mezi kterými možnostmi kolísá.

Krok 2, týkající se funkcí šablon, ještě nebyl aktivován. Přidejme do našeho seznamu další dvě funkce.

( 7 ) šablona < typename T > neplatný f ( T , T ); ( 8 ) šablona < typename T > void f ( T , název typu T :: iterátor );

Funkce 7 bude ve čtvrtém kroku vyřazena, protože funkce bez šablony je vždy „silnější“ než šablonová.

Šablona 8 je daleko od našeho úkolu, protože je navržena pro určitou třídu, která má typ iterator. Druhým krokem je SFINAE : kompilátor říká, že T = int, pokusí se nahradit intv šabloně a ty šablony, u kterých náhrada nevedla k úspěchu, jsou zahozeny. Proto neúspěšné nahrazení není chybou .

Příklad odrazu při kompilaci pomocí SFINAE

Tento příklad se zkompiluje i v C++03 .

#include <iostream> #include <vektor> #include <set> šablona < typenameT > _ třídy DetectFind { struct Fallback { int find ; }; // přidat jméno člena "najít" struct Odvozeno : T , Fallback { }; šablona < název typu U , U > struct Kontrola ; typedef char Ano [ 1 ]; // typedef pro pole o velikosti jedna. typedef char č. [ 2 ]; // typedef pro pole o velikosti dvě. šablona < typename U > static No & func ( Check < int Fallback ::* , & U :: find > * ); šablona < typename U > statické Ano & func (...); veřejnost : typedef DetectFind typ ; enum { hodnota = sizeof ( func < Derived > ( 0 )) == sizeof ( Yes ) }; }; int main () { std :: cout << DetectFind < std :: vector < int >> :: hodnota << ' ' << DetectFind < std :: set < int > >:: hodnota << std :: endl ; návrat 0 ; }

Jak to funguje: k řešení přetížení dochází v řetězci a konkrétní typ je silnější než proměnné argumenty . Vzhledem k tomu, že pod , není potřeba instanciovat šablonové funkce, stačí dosadit typy - funkce tedy mají pouze hlavičky bez těl. Druhá funkce, která vrací typ , bude vždy nahrazena, ale co ta první? sizeof(func<Derived>(0))Check<int Fallback::*, &U::find> *...funcsizeofYes

Bude nahrazen, pokud bude existovat typ šablony (protože pod ukazatelem není důležitý přesný typ, hlavní je existence). První parametr šablony je typ, druhý je konstanta tohoto typu. Ukazatel na -pole objektu se bere jako typ (ve skutečnosti posun od začátku objektu k poli), jako konstanta, ukazatel na pole . Konstanta bude definována a správného typu, pokud je jediné pole převzato z objektu  – to znamená, že neexistuje žádné jiné vypůjčené z . CheckCheckintFallbackfindDerived::findFallbackfindT

Poznámky

Odkazy

  • SFINAE  (anglicky) . cppreference.com. Získáno 9. ledna 2020. Archivováno z originálu dne 6. května 2021.
V Rusku