Zajímavosti z C++: Reference na funkce a na pole

Od Petr Zemek, 2015-11-22

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.

Reference na funkce

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].

Reference na pole

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).

Funkce na zjištění velikosti pole

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.

Obsah tohoto pole je soukromý a nebude veřejně zobrazen.

Filtrované HTML (využíváno)

  • Povolené HTML značky: <a href hreflang> <em> <strong> <cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <table>
  • Zvýraznění syntaxe kódu lze povolit přes následující značky: <code>, <blockcode>, <bash>, <c>, <cpp>, <haskell>, <html>, <java>, <javascript>, <latex>, <perl>, <php>, <python>, <ruby>, <rust>, <sql>, <text>, <vim>, <xml>, <yaml>.
  • Řádky a odstavce se zalomí automaticky.
  • Webové a e-mailové adresy jsou automaticky převedeny na odkazy.
CAPTCHA
2 + 5 =
Vyřešte tento jednoduchý matematický příklad a vložte výsledek. Např. pro 1+3 vložte 4.
Nějak se mi tady rozmohl spam, takže poprosím o ověření.