Sdílet prostřednictvím


Funkce (C++)

Funkce je blok kódu, který provádí určitou operaci. Funkce může volitelně definovat vstupní parametry, které volajícím umožňují předávat argumenty do funkce. Funkce může volitelně vrátit hodnotu jako výstup. Funkce jsou užitečné pro zapouzdření běžných operací v jednom opakovaně použitelném bloku, ideálně s názvem, který jasně popisuje, co funkce dělá. Následující funkce přijímá dvě celá čísla z volajícího a vrátí jejich součet; a a b jsou parametry typu int.

int sum(int a, int b)
{
    return a + b;
}

Funkci lze vyvolat nebo volat z libovolného počtu míst v programu. Hodnoty předané funkci jsou argumenty, jejichž typy musí být kompatibilní s typy parametrů v definici funkce.

int main()
{
    int i = sum(10, 32);
    int j = sum(i, 66);
    cout << "The value of j is" << j << endl; // 108
}

Neexistuje žádné praktické omezení délky funkce, ale dobrý návrh má za cíl funkce, které provádějí jeden dobře definovaný úkol. Složité algoritmy by se měly rozdělit na snadno pochopitelné jednodušší funkce, kdykoli je to možné.

Funkce definované v oboru třídy se nazývají členské funkce. V jazyce C++ lze na rozdíl od jiných jazyků definovat funkci také v oboru názvů (včetně implicitního globálního oboru názvů). Tyto funkce se označují jako bezplatné nebo nečlenské funkce, které se používají ve standardní knihovně.

Funkce mohou být přetíženy, což znamená, že různé verze funkce mohou sdílet stejný název, pokud se liší podle počtu a/nebo typu formálních parametrů. Další informace naleznete v tématu Přetížení funkce.

Části deklarace funkce

Minimální deklarace funkce se skládá z návratového typu, názvu funkce a seznamu parametrů (který může být prázdný) spolu s volitelnými klíčovými slovy, která poskytují kompilátoru další pokyny. Následující příklad je deklarace funkce:

int sum(int a, int b);

Definice funkce se skládá z deklarace a těla, což je veškerý kód mezi složenými závorkami:

int sum(int a, int b)
{
    return a + b;
}

Deklarace funkce následovaná středníkem se může v programu objevit na více místech. Musí se zobrazit před všemi voláními této funkce v každé jednotce překladu. Definice funkce se musí v programu zobrazovat pouze jednou podle pravidla jedné definice (ODR).

Požadované části deklarace funkce jsou:

  1. Návratový typ, který určuje typ hodnoty, kterou funkce vrátí, nebo void pokud není vrácena žádná hodnota. V jazyce C++11 je platný návratový typ, auto který kompilátoru dává pokyn, aby odvodil typ z návratového příkazu. V jazyce C++14 decltype(auto) je také povoleno. Další informace naleznete v části Odpočty typu v návratových typech níže.

  2. Název funkce, který musí začínat písmenem nebo podtržítkem a nesmí obsahovat mezery. Obecně platí, že úvodní podtržítka v názvech funkcí standardní knihovny označují soukromé členské funkce nebo nečlenské funkce, které nejsou určené pro použití vaším kódem.

  3. Seznam parametrů, složená složená závorka, čárkami oddělená sada nula nebo více parametrů, které určují typ a volitelně místní název, pomocí kterého mohou být hodnoty přístupné uvnitř těla funkce.

Volitelné části deklarace funkce jsou:

  1. constexpr, což označuje, že návratová hodnota funkce je konstantní hodnota lze vypočítat v době kompilace.

    constexpr float exp(float x, int n)
    {
        return n == 0 ? 1 :
            n % 2 == 0 ? exp(x * x, n / 2) :
            exp(x * x, (n - 1) / 2) * x;
    };
    
  2. Jeho specifikace propojení, extern nebo static.

    //Declare printf with C linkage.
    extern "C" int printf( const char *fmt, ... );
    
    

    Další informace najdete v tématu Jednotky překladu a propojení.

  3. inline, který dává kompilátoru pokyn, aby nahradil každé volání funkce vlastním kódem funkce. Inlining může přispět k výkonu ve scénářích, kdy se funkce spouští rychle a vyvolává se opakovaně v části kódu kritické pro výkon.

    inline double Account::GetBalance()
    {
        return balance;
    }
    

    Další informace naleznete v tématu Vložené funkce.

  4. Výraz noexcept , který určuje, zda funkce může vyvolat výjimku. V následujícím příkladu funkce nevyvolá výjimku, pokud se is_pod výraz vyhodnotí jako true.

    #include <type_traits>
    
    template <typename T>
    T copy_object(T& obj) noexcept(std::is_pod<T>) {...}
    

    Další informace najdete na webu noexcept.

  5. (Pouze členské funkce) Kvalifikátory cv, které určují, zda je const funkce nebo volatile.

  6. (pouze členské funkce) virtual, overridenebo final. virtual určuje, že funkci lze přepsat v odvozené třídě. override znamená, že funkce v odvozené třídě přepisuje virtuální funkci. final znamená, že funkci nelze přepsat v žádné další odvozené třídě. Další informace najdete v tématu Virtuální funkce.

  7. (pouze členské funkce) static použitý u členské funkce znamená, že funkce není přidružená k žádným instancím objektu třídy.

  8. (Pouze nestatické členské funkce) Kvalifikátor ref, který určuje kompilátor, který přetížení funkce zvolit, když implicitní parametr objektu (*this) je odkaz rvalue vs. odkaz lvalue. Další informace naleznete v tématu Přetížení funkce.

Definice funkcí

Definice funkce se skládá z deklarace a těla funkce uzavřené ve složených závorkách, které obsahují deklarace proměnných, příkazy a výrazy. Následující příklad ukazuje úplnou definici funkce:

    int foo(int i, std::string s)
    {
       int value {i};
       MyClass mc;
       if(strcmp(s, "default") != 0)
       {
            value = mc.do_something(i);
       }
       return value;
    }

Proměnné deklarované uvnitř těla se nazývají místní proměnné nebo místní proměnné. Při ukončení funkce vyjdou mimo rozsah; funkce by proto nikdy neměla vracet odkaz na místní!

    MyClass& boom(int i, std::string s)
    {
       int value {i};
       MyClass mc;
       mc.Initialize(i,s);
       return mc;
    }

const a constexpr – funkce

Členovou funkci můžete deklarovat tak, aby const určila, že funkce nemůže měnit hodnoty žádných datových členů ve třídě. Deklarováním členské funkce jako constpomáháte kompilátoru vynucovat správnost const-correct. Pokud se někdo omylem pokusí upravit objekt pomocí funkce deklarované jako const, je vyvolána chyba kompilátoru. Další informace najdete v tématu const.

Deklarujte funkci jako, když constexpr je možné určit hodnotu, kterou vytváří, v době kompilace. Funkce constexpr obvykle provádí rychleji než běžná funkce. Další informace najdete na webu constexpr.

Šablony funkcí

Šablona funkce je podobná šabloně třídy; generuje konkrétní funkce na základě argumentů šablony. V mnoha případech je šablona schopna odvodit argumenty typu, a proto není nutné je explicitně specifikovat.

template<typename Lhs, typename Rhs>
auto Add2(const Lhs& lhs, const Rhs& rhs)
{
    return lhs + rhs;
}

auto a = Add2(3.13, 2.895); // a is a double
auto b = Add2(string{ "Hello" }, string{ " World" }); // b is a std::string

Další informace najdete v tématu Šablony funkcí.

Parametry funkce a argumenty

Funkce má seznam parametrů oddělený čárkami s nulou nebo více typů, z nichž každý má název, pomocí kterého může být přístupný uvnitř těla funkce. Šablona funkce může zadat více parametrů typu nebo hodnoty. Volající předává argumenty, což jsou konkrétní hodnoty, jejichž typy jsou kompatibilní se seznamem parametrů.

Ve výchozím nastavení se argumenty předávají funkci hodnotou, což znamená, že funkce obdrží kopii předaného objektu. U velkých objektů může být vytváření kopie nákladné a není vždy nutné. Chcete-li způsobit předání argumentů odkazem (konkrétně odkazem lvalue), přidejte k parametru kvantifikátor odkazu:

void DoSomething(std::string& input){...}

Když funkce upraví argument předaný odkazem, upraví původní objekt, nikoli místní kopii. Pokud chcete zabránit funkci v úpravě takového argumentu, opravte parametr jako const&:

void DoSomething(const std::string& input){...}

C++11: Pokud chcete explicitně zpracovat argumenty předané odkazem rvalue nebo lvalue-reference, použijte u parametru dvojitý ampersand k označení univerzálního odkazu:

void DoSomething(const std::string&& input){...}

Funkce deklarovaná jediným klíčovým slovem void v seznamu deklarací parametrů nepřijímá žádné argumenty, pokud je klíčové slovo void prvním a jediným členem seznamu deklarací argumentů. Argumenty typu void jinde v seznamu generují chyby. Příklad:

// OK same as GetTickCount()
long GetTickCount( void );

I když není možné zadat void argument s výjimkou zde popsaného, mohou se typy odvozené od typu void (například ukazatele na void a pole) objevit kdekoli v seznamu deklarací argumentů void.

Výchozí argumenty

Poslednímu parametru nebo parametrům v podpisu funkce může být přiřazen výchozí argument, což znamená, že volající může při volání funkce vynechat argument, pokud nechce zadat jinou hodnotu.

int DoSomething(int num,
    string str,
    Allocator& alloc = defaultAllocator)
{ ... }

// OK both parameters are at end
int DoSomethingElse(int num,
    string str = string{ "Working" },
    Allocator& alloc = defaultAllocator)
{ ... }

// C2548: 'DoMore': missing default parameter for parameter 2
int DoMore(int num = 5, // Not a trailing parameter!
    string str,
    Allocator& = defaultAllocator)
{...}

Další informace naleznete v tématu Výchozí argumenty.

Návratové typy funkcí

Funkce nemusí vracet jinou funkci nebo předdefinované pole; ale může vracet ukazatele na tyto typy nebo lambda, které vytváří objekt funkce. S výjimkou těchto případů může funkce vrátit hodnotu libovolného typu, který je v oboru, nebo může vrátit žádnou hodnotu, v takovém případě návratový typ je void.

Koncové návratové typy

"běžný" návratový typ se nachází na levé straně podpisu funkce. Koncový návratový typ se nachází na pravé straně podpisu a je před operátorem -> . Koncové návratové typy jsou zvlášť užitečné v šablonách funkcí, pokud typ návratové hodnoty závisí na parametrech šablony.

template<typename Lhs, typename Rhs>
auto Add(const Lhs& lhs, const Rhs& rhs) -> decltype(lhs + rhs)
{
    return lhs + rhs;
}

Pokud auto se používá ve spojení s koncovým návratovým typem, slouží pouze jako zástupný symbol pro cokoli, co výraz decltype vytvoří, a sám neprovádí odpočty typů.

Místní proměnné funkce

Proměnná deklarovaná uvnitř těla funkce se nazývá místní proměnná nebo jednoduše místní. Nestatické místní hodnoty jsou viditelné pouze v těle funkce a pokud jsou deklarovány v zásobníku, při ukončení funkce vyjdou mimo rozsah. Když vytvoříte místní proměnnou a vrátíte ji podle hodnoty, kompilátor může obvykle provést optimalizaci pojmenované návratové hodnoty, aby se zabránilo zbytečným operacím kopírování. Pokud vrátíte místní proměnnou odkazem, kompilátor vydá upozornění, protože jakýkoli pokus volajícího o použití tohoto odkazu nastane po zničení místního objektu.

V jazyce C++ může být místní proměnná deklarována jako statická. Proměnná je viditelná pouze uvnitř těla funkce, ale jedna kopie proměnné existuje pro všechny instance funkce. Místní statické objekty jsou při ukončení zrušeny pomocí atexit. Pokud statický objekt nebyl vytvořen, protože tok řízení programu obešel jeho deklaraci, není proveden žádný pokus o zničení tohoto objektu.

Odvozování typů ve návratových typech (C++14)

V jazyce C++14 můžete kompilátoru dát auto pokyn, aby odvozoval návratový typ z těla funkce, aniž by musel zadávat koncový návratový typ. Všimněte si, že auto vždy se odpisuje na návrat po hodnotě. Slouží auto&& k pokynu kompilátoru, aby odkaz odvodil.

V tomto příkladu auto bude dedukována jako kopie hodnoty bez const součtu lhs a rhs.

template<typename Lhs, typename Rhs>
auto Add2(const Lhs& lhs, const Rhs& rhs)
{
    return lhs + rhs; //returns a non-const object by value
}

Všimněte si, že auto neuchová kont-ness typu, který odvodí. Pro funkce předávání, jejichž návratová hodnota musí zachovat const-ness nebo ref-ness jejích argumentů, můžete použít decltype(auto) klíčové slovo, které používá decltype pravidla odvozování typu a zachovává všechny informace o typu. decltype(auto) může být použita jako běžná návratová hodnota na levé straně nebo jako koncová návratová hodnota.

Následující příklad (na základě kódu z N3493) ukazuje decltype(auto) použití k zajištění dokonalého předávání argumentů funkce v návratovém typu, který není známý, dokud se šablona nes instancí.

template<typename F, typename Tuple = tuple<T...>, int... I>
decltype(auto) apply_(F&& f, Tuple&& args, index_sequence<I...>)
{
    return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
}

template<typename F, typename Tuple = tuple<T...>,
    typename Indices = make_index_sequence<tuple_size<Tuple>::value >>
   decltype( auto)
    apply(F&& f, Tuple&& args)
{
    return apply_(std::forward<F>(f), std::forward<Tuple>(args), Indices());
}

Vrácení více hodnot z funkce

Funkce může vrátit více než jednu hodnotu různými způsoby:

  1. Zapouzdřte hodnoty v pojmenované třídě nebo objektu struktury. Vyžaduje, aby byla definice třídy nebo struktury viditelná volajícímu:

    #include <string>
    #include <iostream>
    
    using namespace std;
    
    struct S
    {
        string name;
        int num;
    };
    
    S g()
    {
        string t{ "hello" };
        int u{ 42 };
        return { t, u };
    }
    
    int main()
    {
        S s = g();
        cout << s.name << " " << s.num << endl;
        return 0;
    }
    
  2. Vrátí objekt std::tuple nebo std::p air:

    #include <tuple>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    tuple<int, string, double> f()
    {
        int i{ 108 };
        string s{ "Some text" };
        double d{ .01 };
        return { i,s,d };
    }
    
    int main()
    {
        auto t = f();
        cout << get<0>(t) << " " << get<1>(t) << " " << get<2>(t) << endl;
    
        // --or--
    
        int myval;
        string myname;
        double mydecimal;
        tie(myval, myname, mydecimal) = f();
        cout << myval << " " << myname << " " << mydecimal << endl;
    
        return 0;
    }
    
  3. Visual Studio 2017 verze 15.3 a novější (dostupné v /std:c++17 režimu a novější): Používejte strukturované vazby. Výhodoustrukturovaných V příkazu auto[x, y, z] = f(); závorky představují a inicializují názvy, které jsou v oboru pro celý blok funkce.

    #include <tuple>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    tuple<int, string, double> f()
    {
        int i{ 108 };
        string s{ "Some text" };
        double d{ .01 };
        return { i,s,d };
    }
    struct S
    {
        string name;
        int num;
    };
    
    S g()
    {
        string t{ "hello" };
        int u{ 42 };
        return { t, u };
    }
    
    int main()
    {
        auto[x, y, z] = f(); // init from tuple
        cout << x << " " << y << " " << z << endl;
    
        auto[a, b] = g(); // init from POD struct
        cout << a << " " << b << endl;
        return 0;
    }
    
  4. Kromě použití samotné návratové hodnoty můžete "vrátit" hodnoty definováním libovolného počtu parametrů pro použití předávacího odkazu, aby funkce mohl upravit nebo inicializovat hodnoty objektů, které volající poskytuje. Další informace naleznete v tématu Argumenty funkce typu Odkaz.

Ukazatelé funkcí

Jazyk C++ podporuje ukazatele funkcí stejným způsobem jako jazyk C. Bezpečnější alternativou je ale obvykle použití objektu funkce.

typedef Doporučuje se deklarovat alias pro typ ukazatele funkce, pokud deklarujete funkci, která vrací typ ukazatele funkce. Například

typedef int (*fp)(int);
fp myFunction(char* s); // function returning function pointer

Pokud to neuděláte, může být správná syntaxe deklarace funkce odvozena ze syntaxe deklarátoru ukazatele funkce nahrazením identifikátoru (fp v předchozím příkladu) názvem funkcí a seznamem argumentů, jak je znázorněno níže:

int (*myFunction(char* s))(int);

Předchozí deklarace je ekvivalentní deklaraci pomocí typedef předchozí deklarace.

Viz také

Přetížení funkce
Funkce se seznamy argumentů proměnných
Explicitně přednastavené a odstraněné funkce
Vyhledávání názvu závislého na argumentu (Koenig) ve funkcích
Výchozí argumenty
Vložené funkce