decltype

Z Wikipedii, wolnej encyklopedii

decltypeoperator pozwalający na uzyskanie typu wyrażenia w języku programowania C++. Został on wprowadzony w wersji C++11 standardu C++. Jego głównym przeznaczeniem jest programowanie uogólnione, w którym często trudno, jeśli w ogóle jest to możliwe, określić typy zależne od parametrów szablonu.

Wzrost popularności technik programowania uogólnionego następował od lat dziewięćdziesiątych XX wieku. Wówczas zauważona została potrzeba mechanizmu do wyznaczania typu. Wielu producentów kompilatorów wdrożyło własne wersje takiego operatora, zazwyczaj nazywanego typeof oraz opracowało pewne przenośne implementacje o ograniczonej funkcjonalności, w oparciu o istniejące funkcje języka. W 2002 roku Bjarne Stroustrup zaproponował, aby standardową formę takiego operatora dodać do języka C++ i zasugerował nazwę „decltype”, aby odzwierciedlić, że operator zwraca „deklarowany typ” wyrażenia.

Semantyka decltype została tak zaprojektowana, aby zaspokoić potrzeby autorów bibliotek z kodem uogólnionym, jak również początkujących programistów. W ogólności, typ określony za pomocą tego operatora zgadza się z typem obiektu lub funkcji zadeklarowanym w kodzie źródłowym. Podobnie jak w przypadku operatora sizeof, operand decltype nie jest wykonywany.

Motywacja[edytuj | edytuj kod]

Po wprowadzeniu szablonów w języku programowania C++ i tym samym umożliwieniu programowania uogólnionego, zapoczątkowanego przez standardową bibliotekę szablonów, zauważono potrzebę mechanizmu uzyskiwania typu wyrażenia, powszechnie określanego mianem typeof. W programowaniu uogólnionym często trudno określić typ zależny od parametrów szablonu lub jest to niemożliwe[1][2], zwłaszcza gdy chodzi o typ wyniku funkcji realizowanej w definicji szablonu[1].

Wielu producentów dostarcza operator typeof jako rozszerzenie kompilatora[3]. Już w 1997 roku, zanim C++ został w pełni ustandaryzowany, Brian Parker zaproponował przenośne rozwiązanie w oparciu o operator sizeof[3]. Następnie pracę tę poszerzył Bill Gibbons, który stwierdził, że ta technika ma kilka ograniczeń i w ogólności jest mniej skuteczna niż rzeczywisty mechanizm typeof[3]. W październiku 2000 roku w artykule w Dr. Dobb's Journal, Andrei Alexandrescu zauważył że, posiadanie operatora typeof znacznie by uprościło pisanie i rozumienie szablonów[4]. Wspomniał również, że typeof i sizeof używają takiego samego wsparcia wewnętrznego, ponieważ operator sizeof i tak musi najpierw określić typ[4]. Andrew Koenig i Barbara E. Moo także dostrzegli przydatność wbudowanego ułatwienia typeof, z zastrzeżeniem, że częste jego użycie może powodować pewne subtelne błędy, oraz że istnieją pewne problemy, których on nie rozwiązuje[5]. Scharakteryzowali użycie konwencji typu, jak typedef dostarczany przez standardową bibliotekę szablonów jako technikę bardziej skuteczną i ogólną[5]. Jednak Steve Dewhurst argumentował, że takie konwencje są kosztowne do zaprojektowania i ogłoszenia, a także byłoby o wiele łatwiej po prostu wyodrębnić typ wyrażenia[6]. W artykule z 2011 roku na temat C++0x, Koenig i Moo przewidzieli, że decltype będzie szeroko stosowany, aby codzienne programowanie było łatwiejsze w pisaniu[7].

W 2002 roku Bjarne Stroustrup zasugerował rozszerzenie języka C++ o mechanizm pozwalający uzyskać typ z wyrażenia i zainicjować obiekt bez określania typu[1]. Stroustrup zaobserwował, że problemem może być semantyka odrzucania referencji oferowana przez operator typeof zapewniana przez kompilatory GCC i EDG[1]. Z drugiej strony, operator zwracający typ referencyjny oparty o l-wartościowość wyrażenia został uznany za zbyt skomplikowany. Początkowy wniosek do komisji standaryzacji C++ nakreślił kombinację dwóch wariantów; operator zwracałby typ referencyjny tylko jeśli zadeklarowany typ wyrażenia zawierałby referencję. Aby podkreślić, że dedukowany typ pochodzi od „deklarowanego typu” wyrażenia, nazwa zaproponowanego operatora to decltype[1].

Jednym z najczęściej cytowanych motywów wprowadzenia proponowanego operatora decltype była możliwość zapisu w szablonach idealnych funkcji przekierowujących[8]. Jest czasami pożądane, aby utworzyć uogólnioną funkcję przekierowującą, która zwraca taki sam typ wyniku jak funkcja docelowa bez względu na rodzaj jego tworzenia. Bez decltype, nie jest to w ogólności możliwe[8]. Przykład stosujący również nową składnie deklaracji i definicji funkcji[8]:

int& foo(int& i);
float foo(float& f);

template <class T> auto transparent_forwarder(T& t) > decltype(foo(t)) {
  return foo(t);
}

Istotą operatora decltype w powyższym przykładzie jest zachowanie informacji o tym czy docelowa funkcja zwraca typ referencyjny[9].

Semantyka[edytuj | edytuj kod]

Podobnie jak w operatorze sizeof, operand decltype nie jest wykonywany[10]. Nieformalnie, typ zwracany przez decltype jest wydedukowany następująco[1]:

  1. jeśli wyrażenie e odwołuje się do zmiennej w zakresie lokalnym lub w przestrzeni nazw, statycznej zmiennej klasy lub parametru funkcji, to wynikiem jest zadeklarowany typ zmiennej lub parametru
  2. jeśli e jest wywołaniem funkcji lub użyciem przeciążonego operatora, decltype(e) oznacza zadeklarowany typ wyniku tej funkcji
  3. w przeciwnym razie, jeśli e to l-wartość, decltype(e) to T&, gdzie T jest typem e; jeśli e jest r-wartością, to wynikiem jest T.

Semantyka została tak zaprojektowana, aby spełnić oczekiwania autorów bibliotek z kodem uogólnionym, a ponadto jest intuicyjna dla początkujących programistów, ponieważ zwracany typ przez decltype zawsze dokładnie zgadza się z typem obiektu lub funkcji zadeklarowanym w kodzie źródłowym[1]. Bardziej formalnie, reguła #1 ma zastosowanie do beznawiasowych id-expression[a] i wyrażeń dostępu do składowych klasy[12]. Dla wywołań funkcji wydedukowany typ to typ zwracany przez statycznie wybraną funkcję, określoną według reguł przeciążania funkcji[13]. Przykład[12]:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1; // typ to const int&&
decltype(i) x2; // typ to int
decltype(a->x) x3; // typ to double
decltype((a->x)) x4; // typ to const double&

Powodem różnic w ostatnich dwóch wywołaniach decltype jest to, że wyrażenie w nawiasie (a->x) nie jest ani id-expression ani dostępem do skladowych klasy, a stąd nie oznacza nazwanego obiektu[14]. Ponieważ to wyrażenie jest l-wartością, jego wydedukowany typ jest referencją do typu wyrażenia, czyli const double&[10].

W grudniu 2008 Jaakko Järvi zgłosił problem do komisji, że decltype nie można zastosować do utworzenia qualified-id[15][b], co jest niespójne z intencją, że decltype powinien być traktowany tak jakby był typedef-name[16][c]. W odniesieniu do Committee Draft for C++0x, członek japońskiej ISO zauważył, że operator zakresu (::) nie może być zastosowany do decltype, a powinien. Byłoby to pożyteczne, żeby uzyskać typ bazowy w typie pochodnym z wystąpienia obiektu jak w przykładzie[18]:

vector<int> v;
decltype(v)::value_type i = 0; // int i = 0;

Te i podobne kwestie odnoszące się do treści wstrzymującej stosowalność decltype w deklaracji klasy potomnej i w wywołaniu destruktora, zostały zaadresowane i zagłosowane przez Davida Vandevoorde'a w dokumencie roboczym z marca 2010[19][20].

Dostępność[edytuj | edytuj kod]

decltype jest zawarty w obecnej wersji standardu języka C++, C++11[12]. Jest zapewniany przez wiele kompilatorów jako rozszerzenie. Kompilator Microsoft Visual C++ 2010 dostarcza operator decltype, który dość wiernie naśladuje specyfikację opisaną we wniosku do komitetu standaryzującego. Może on być stosowany zarówno w kodzie zarządzanym jak i natywnym[9]. Dokumentacja stanowi, że jest „użyteczny głównie dla programistów, którzy piszą biblioteki szablonów”[9]. decltype został dodany do głównej linii kompilatorów C++ GCC w wersji 4.3[21], wydanych 5 marca 2008[22]. Operator ten jest również obecny w C++ Builder 2009[23] od CodeGear i Clang[24].

Uwagi[edytuj | edytuj kod]

  1. id-expression to identyfikator lub wyrażenie pozwalające na wskazanie dowolnego elementu języka C++[11]
  2. qualified-id to identyfikator lub wyrażenie (podzbiór id-expression), które jest poprzedzone operatorem zakresu[11]
  3. typedef-name to nazwa typu, która może być wzbogacona o modyfikatory definiujące typy pochodne (wskaźnik, tablica itp.)[17]

Przypisy[edytuj | edytuj kod]

  1. a b c d e f g Douglas Gregor i inni, Decltype and auto [online], ISO/IEC JTC1/SC22/WG21 – The C++ Standards Committee, 28 kwietnia 2003 [dostęp 2009-08-13] (ang.).
  2. Danny Kalev, Clean Up Function Syntax Mess with ''decltype'' [online], DevX.com, 8 maja 2008 [dostęp 2009-09-04] (ang.).
  3. a b c Bill Gibbons, A Portable "typeof" Operator [online], Dr. Dobb's Journal, 1 listopada 2000 [dostęp 2012-09-13] (ang.).
  4. a b Andrei Alexandrescu, Generic<Programming>: Mappings between Types and Values [online], Dr. Dobb's Journal, 1 października 2000 [dostęp 2012-09-13] (ang.).
  5. a b Andrew Koenig, Barbara E. Moo, C++ Made Easier: Naming Unknown Types [online], Dr. Dobb's Journal, 1 lutego 2002 [dostęp 2012-09-13] (ang.).
  6. Steve Dewhurst, Common Knowledge: A Bitwise typeof Operator, Part 1 [online], Dr. Dobb's Journal, 1 sierpnia 2000 [dostęp 2012-09-13] (ang.).
  7. Andrew Koenig, Barbara E. Moo, 4 Useful New Features in C++0x [online], Dr. Dobb's Journal, 19 lipca 2011 [dostęp 2012-01-12] (ang.).
  8. a b c Gabriel Dos Reis, Jaakko Järvi, Bjarne Stroustrup, Decltype and auto (revision 4) [online], ISO/IEC JTC1/SC22/WG21 – The C++ Standards Committee, 12 października 2004 [dostęp 2012-09-14] (ang.).
  9. a b c decltype Operator [online], Microsoft Corporation [dostęp 2009-09-04] (ang.).
  10. a b Gabriel Dos Reis, Jaakko Järvi, Bjarne Stroustrup, Decltype (revision 7): proposed wording [online], ISO/IEC JTC1/SC22/WG21 – The C++ Standards Committee, 18 lipca 2007 [dostęp 2012-09-14] (ang.).
  11. a b Identifier expressions (C++ only) [online], IBM Knowledge Center [dostęp 2016-08-06] (ang.).
  12. a b c Pete Becker, Working Draft, Standard for Programming Language C++ [online], ISO/IEC JTC1/SC22/WG21 – The C++ Standards Committee [dostęp 2012-09-14] (ang.).
  13. William M. Miller, C++ Standard Core Language Defect Reports, Revision 65 [online], ISO/IEC JTC1/SC22/WG21 – The C++ Standards Committee, 3 sierpnia 2009 [dostęp 2009-09-15] (ang.).
  14. William M. Miller, C++ Standard Core Language Closed Issues, Revision 65 [online], ISO/IEC JTC1/SC22/WG21 – The C++ Standards Committee, 3 sierpnia 2009 [dostęp 2012-09-14] (ang.).
  15. William M. Miller, C++ Standard Core Language Active Issues, Revision 66 [online], ISO/IEC JTC1/SC22/WG21 – The C++ Standards Committee, 29 września 2009 [dostęp 2012-09-14] (ang.).
  16. Gabriel Dos Reis, Jaakko Järvi, Bjarne Stroustrup, Decltype (revision 6): proposed wording [online], ISO/IEC JTC1/SC22/WG21 – The C++ Standards Committee, 5 listopada 2006 [dostęp 2009-10-03] (ang.).
  17. Type names [online], IBM Knowledge Center [dostęp 2016-08-06] (ang.).
  18. William M. Miller, C++ CD1 Comment Status [online], ISO/IEC JTC1/SC22/WG21 – The C++ Standards Committee, 3 sierpnia 2009 [dostęp 2012-09-14] (ang.).
  19. William M. Miller, C++ Standard Core Language Defect Reports, Revision 69 [online], ISO/IEC JTC1/SC22/WG21 – The C++ Standards Committee, 29 marca 2010 [dostęp 2012-09-14] (ang.).
  20. Daveed Vandevoorde, Core issues 743 and 950: Additional decltype(...) uses [online], ISO/IEC JTC1/SC22/WG21 – The C++ Standards Committee, 3 lutego 2010 [dostęp 2012-09-14] (ang.).
  21. C++0x Support in GCC [online], Free Software Foundation, 27 sierpnia 2009 [dostęp 2012-09-14].
  22. GCC 4.3 Release Series [online], Free Software Foundation, 13 sierpnia 2009 [dostęp 2012-09-14].
  23. Type Specifier decltype (C++0x) [online], Embarcadero Technologies [dostęp 2012-09-14] [zarchiwizowane z adresu 2015-04-20].
  24. Gregor Douglas, New C++0x feature support in Clang [online], 26 stycznia 2011 [dostęp 2012-09-14] [zarchiwizowane z adresu 2014-09-06].

Linki zewnętrzne[edytuj | edytuj kod]