• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

typename oraz using w metaprogramowaniu

Object Storage Arubacloud
0 głosów
113 wizyt
pytanie zadane 3 lipca 2020 w C i C++ przez Patelniaszczy Nowicjusz (170 p.)

Cześć!

Od jakiegoś czasu poszukuję zrozumiałego wyjaśnienia pewnych kwestii których ogarnięcie pozwoli mi zagłębić się w koncepcje metaprogramowania.

Mianowicie chodzi mi o słowo kluczowe typename oraz using w konkretnych przypadkach. Generalnie wiem tyle że typename jest używane do określania parametrów szablonowych np: template<typename T> int foo(T *t){return t;}, zaś using służy do ustalenia domyślnie używanych przestrzeni nazw np: using namespace std;, lub jak w przypadku typedef stworzenia własnego aliasu do trudniejszego do napisania typu danej np: using flags = std::ios base::fmtflags;

Mój problem polega na zrozumieniu bardziej skomplikowanych przykładów jak ten poniżej

template<typename T>
using Invoke = typename T::type; 
//Czemu tutaj występuje typename i czy T::type jest jakąś zmienną w klasie T?
template<typename Condition>
using EnableIf = Invoke<std::enable_if<Condition::value>>; 
//W jaki sposób to wyrażenie wiąże się z poprzednim, //T::type<std::enable_if<Condition::value>>;??
template<typename T, typename = EnableIf<std::is_polymorphic<T>>> 
//Co oznacza przypisanie czegoś do typename w takiej formie??
int fpoly_only(T t) { return 1; }
 
struct S { virtual ~S() {} };
 
int main() 
{
    S s;
    fpoly_only(s); 
}

Z góry dziękuję za pomoc,

Pozdrawiam

1 odpowiedź

+1 głos
odpowiedź 3 lipca 2020 przez adrian17 Ekspert (345,160 p.)

Ogólnie to wygląda jak trochę starsza metoda robienia SFINAE z enable_if w C++11.

// Czemu tutaj występuje typename i czy T::type jest jakąś zmienną w klasie T?
template<typename T>
using Invoke = typename T::type; 

To jest alias template. Zgodnie z intuicją: dla typu T, Invoke<T> to typ T::type.

Drugi `typename` jest potrzebny, ponieważ... w sumie najlepiej podpowie kompilator, gdy tego nie dasz :)

<source>:5:16: error: need 'typename' before 'T::type' because 'T' is a dependent scope
    5 | using Invoke = T::type;
      |                ^
      |                typename 

//W jaki sposób to wyrażenie wiąże się z poprzednim

//T::type<std::enable_if<Condition::value>>;??

Na odwrót. To jest równoważne z

std::enable_if<Condition::value>>::type

//Co oznacza przypisanie czegoś do typename w takiej formie??

To jest analogiczne do domyślnych argumentów funkcji:

void f(int argument, int = 5) // w deklaracji mozesz nie nazwac argumentu, ale mozesz dac mu wartosc domyslna

ten argument Cię nie interesuje, interesuje tylko sama obecność tego enable_if żeby potencjalnie zaskoczyło SFINAE.

W każdym razie... ogólnie ta złożoność została schowana w bibliotekę standardową.

Teraz możesz napisać:

// c++14
template<typename T, typename = std::enable_if_t<std::is_polymorphic<T>::value>>
// c++17
template<typename T, typename = std::enable_if_t<std::is_polymorphic_v<T>>> 

 

komentarz 5 lipca 2020 przez Patelniaszczy Nowicjusz (170 p.)

Z reguły nie szukam nadziei na polskich forach, ponieważ po konfrontacji z forami typu elektroda.pl można się spodziewać że forumowicze nie pomogą a jeszcze pocisną i wgniotą w gruz xd. Natomiast w tym przypadku zostałem naprawdę mile zaskoczony za co dziękuję :).

Jeśli chodzi o przykład:

// c++14
template<typename T, typename = std::enable_if_t<std::is_polymorphic<T>::value>>
// c++17
template<typename T, typename = std::enable_if_t<std::is_polymorphic_v<T>>>

Moje rozumienie tego jest nieco zamglone, ale staram się ogarnąć co tu się dzieję. Czy mam to rozumieć jako: Jeżeli T jest typem polimorficznym to T może być użyte dalej w funkcji/klasie szablonowej?

void f(int argument, int = 5) // w deklaracji mozesz nie nazwac argumentu, ale mozesz dac mu wartosc domyslna

Powyższy przypadek (przynajmniej moim zdaniem) ma sens jedynie w deklaracji, natomiast:

template<typename T, typename = std::enable_if_t<std::is_polymorphic_v<T>>>

widziałem w użyciu również w definicjach np:

template<class T, typename = std::enable_if_t<std::is_array<T>::value> >
void destroy(T* t)
{
    for(std::size_t i = 0; i < std::extent<T>::value; ++i) {
        destroy((*t)[i]);
    }
}

Obawiam się że bez łopatologicznego wyjaśnienia się nie obejdzie, za co z góry przepraszam.

Kolejne moje pytanie - co taki skomplikowany typename powoduje będąc return type, pomimo że w ciele funkcji nie widzimy żadnego return?

template<class T>
typename std::enable_if<std::is_trivially_default_constructible<T>::value>::type 
    construct(T*) 
{
    std::cout << "default constructing trivially default constructible T\n";
}

Ja wiem że część osób powiedziałaby mi "daj spokój, i tak nie będziesz tego często używać, to już jest zaimplementowane w STL", ale jestem bardzo upartym człowiekiem i po prostu irytuje mnie jak czytam jakiś kod i go nie rozumiem xD. Przykłady zostały wzięte z cppreference.com.

Pozdrawiam i jeszcze raz dziękuje za pomoc

komentarz 5 lipca 2020 przez adrian17 Ekspert (345,160 p.)

Czy mam to rozumieć jako: Jeżeli T jest typem polimorficznym to T może być użyte dalej w funkcji/klasie szablonowej?

Tak. Jeśli T nie jest polimorficzny, enable_if da błąd, załączy się SFINAE i kompilator zignoruje tą specjalizację szablonu.

Powyższy przypadek (przynajmniej moim zdaniem) ma sens jedynie w deklaracji, natomiast (...) widziałem w użyciu również w definicjach np:

To było tylko porównanie składni ;) Ale też się da, po prostu jest jeszcze mniej przydatne:

int f(int = 0) { return 6; }

Ogólnie polecam poczytać o SFINAE :)

Kolejne moje pytanie - co taki skomplikowany typename powoduje będąc return type, pomimo że w ciele funkcji nie widzimy żadnego return?

Tutaj też chodzi o SFINAE. Jeśli typ jest `is_trivially_default_constructible`, to cały ten złożony typename to po prostu void (dlatego też nie trzeba return). Jeśli nie jest `is_trivially_default_constructible`, to znowu nastąpi substitution failure.

 

Podobne pytania

0 głosów
1 odpowiedź 152 wizyt
pytanie zadane 9 kwietnia 2019 w C i C++ przez amelia.cpp Obywatel (1,860 p.)
+2 głosów
1 odpowiedź 741 wizyt
pytanie zadane 11 kwietnia 2021 w C i C++ przez Dawidziu Bywalec (2,610 p.)
0 głosów
1 odpowiedź 408 wizyt
pytanie zadane 6 grudnia 2019 w C i C++ przez Gildin1_2 Gaduła (3,060 p.)

92,624 zapytań

141,482 odpowiedzi

319,822 komentarzy

62,005 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...