Jste zde

Zajímavosti z C++: Když přítomnost závorek hraje roli

Ukážeme si dva případy, kdy přítomnost kulatých závorek v C++ na první pohled působí zbytečně, ale přitom má značný vliv na význam kódu. A nebojte, nebude se jednat o uzávorkování z důvodů priority operátorů :).

První případ

Mějme následující definici proměnné v typu std::vector<int>:

std::vector<int> v((std::vector<int>()));

Pokud bychom závorky odstranili

std::vector<int> v(std::vector<int>());

tak překvapivě nedostaneme definici proměnné typu std::vector<int>, ale deklaraci funkce v, která vrací std::vector<int> a jako parametr má ukazatel na funkci, která nepřebírá žádné parametry a vrací std::vector<int>. Důvodem je pravidlo v C++ říkající, že cokoliv co může být chápáno jako deklarace být chápáno jako deklarace.

Jinou možností je buď použití uniformní inicializace z C++11:

std::vector<int> v{std::vector<int>()};

nebo prosté vynechání inicializátoru:

std::vector<int> v;

jež má v tomto případě stejný efekt: vytvoření prázdného vektoru v.

Druhý případ

O případu výše už jste pravděpodobně slyšeli. Druhý případ bude zajímavější. Někteří programátoři mají zvyk při psaní return ozávorkovávat návratovou hodnotu:

return (a + 5);

Ona přítomnost nadbytečných závorek nám však překvapivě může uškodit. Mějme následující dvě funkce:

decltype(auto) foo1() { auto result = bar(); return result; }
decltype(auto) foo2() { auto result = bar(); return (result); }

Používáme zde decltype(auto) a automatickou dedukci návratového typu funkce z C++14.

Člověk by čekal, že obě funkce budou ekvivalentní. Přeci jen, ve foo2 jsou akorát "nadbytečné závorky". Opak je však pravdou. Zatímco první funkce vrací výsledek hodnotou, ta druhá vrací referenci na lokální proměnnou result... Pokud se pak někdo pokusí modifikovat návratovou hodnotu z foo2(), čeká ho jízda do země nedefinovaného chování, která v nejlepším případě skončí segfaultem (přístup na neplatnou paměť, v tomto případě do stack framu funkce po jejím návratu).

Příčinou je fungování decltype, který je citlivý na přítomnost závorek [C++14, 7.1.6.2, §4]:

int i;
 
decltype(i)   // int
decltype((i)) // int &

Je proto třeba si na přítomnost závorek v souvislosti s decltype dávat pozor.

Přidat komentář