Možnost vytvářet v C a C++ ukazatele na funkce či pole je poměrně dobře známá. V C++ však lze mimo ukazatelů vytvářet i reference na funkce a pole. V příspěvku se krátce podíváme na to, kdy se to může hodit.
Začněme referencemi na funkce. Následující kód ukazuje funkci, která přebírá jako parametr referenci na jinou funkci, kterou následně zavolá:
void callFunc(void (&f)()) { f(); } void myFunc() { std::cout << "myFunc\n"; } int main() { callFunc(myFunc); // Vypíše "myFunc". }
Jaká je však výhoda použití reference místo klasického ukazatele na funkci? Je to podobné, jak u referencí na objekty: odpadá nutnost kontrolovat, zda předaný parametr není nulový ukazatel. Něco jako nulová reference totiž v korektním C++ kódu nemůže existovat [ISO C++14, 8.3.2 §5].
Přejděme nyní na reference na pole. Ty nám umožní předat Céčkové pole do funkce se zachováním všech rozměrů. Když totiž uděláte něco takového
void func(int array[10]) { } int myArray[10]; func(myArray);
tak ve funkci func
přijdete o velikost onoho pole, protože ona deklarace je identická s následující deklarací (detaily):
void func(int *array) { }
Pokud nevěříte, tak si klidně zkuste přeložit následující příklad. Uvidíte, že překlad projde, i když nesedí velikosti polí ;).
void func(int array[10]) { } int myArray[500]; func(myArray); // Překlad projde, protože velikost pole v deklaraci funkce se ignoruje.
Pro zachování velikosti pole a kontrolu překladačem lze použít ukazatel na pole. Pak je ale potřeba do funkce předat adresu onoho pole:
void func(int (*array)[10]) { } int myArray[10]; func(&myArray); // Zde je '&' nutné.
Použitím reference na pole se kód zjednoduší:
void func(int (&array)[10]) { } int myArray[10]; func(myArray); // Nyní bez '&'.
Navíc je práce s polem přes referenci jednodušší, než práce s ukazatel na pole (odpadá nutnost dereference).
V závěru příspěvku se podívejme na možnost využití referencí na pole ke zjištění velikosti Céčkového pole. Programátoři si k tomu typicky vytváří následující makro:
#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
Makro je to z toho důvodu, že kdyby to byla funkce, tak bychom o velikost pole přišli (viz výše). Makro jsou ale zlá a proto se hodí vědět, že od C++11 existuje lepší cesta: využití referencí na pole. S jejich pomocí se lze nutnosti použití makra vyhnout:
template<typename T, std::size_t N> constexpr std::size_t arraySize(T (&)[N]) noexcept { return N; }
Parametr nemá jméno, protože jediné, co potřebujeme, je velikost onoho pole. C++11 je nutné kvůli constexpr
, bez něhož by nebylo možno onu funkci použít ke zjištění velikosti pole za překladu (makro by použít šlo). Pokud by vás zajímaly detaily, tak mrkněte na strany 15--17 v knize Effective Modern C++, odkud jsem funkci výše převzal.
Přidat komentář