Functional (C++)

Functional՝ C++, լեզվի Ստանդարտ գրադարանի վերնագրային ֆայլ, որը իրենից ներկայացնում է կաղապարների հավաքածու ֆունկցիոնալ օբյեկտների հետ աշխատելու համար, ինչպես նաև օգնական դասերի հավաքածու՝ ստանդարտ գրադարանի ալգորիթմներում օգտագործելու համար։

Պատմություն 
Առաջին <functional> վերնագրային ֆայլն առաջացել է 1998 թվականին։ Ի սկզբանե նրա մեջ մտցրել են օժանդակ ֆունկցիոնալ օբյեկտներ, որպեսզի ավելի հեշտ օգտագործել STL։ Ինչպես նաև այստեղ են ներառվել կապողական (binders) և փաթաթողական ֆունկցիաներ, որոնց նպատակն էր հեշտացվել աշխատանքը, երբ ակտիվորեն օգտագործվում էր ցուցիչների փոխանցումը ֆունկցիաներին, այսինքն, երբ աշխատանք են կատարում ֆունկցիաների հետ։
Վերնագրային ֆայլերի Էական փոփոխություններ կատարվեց С++ TR1 ընդարձակման գրադարանում
։ Boost գրադարանից STL են տեղափոխել function, bind, mem_fn, result_of, reference_wrapper, hash դասերը։ Այդ փոփոխությունների մեծամասնությունը, բացառությամբ result_of-ի ընդգրկվեց այդ պահին օգտագործվող C++11 ստանդարտ լեզվի մեջ։ Քանի որ function-ը և bind-ը մեծ մասամբ կրկնօրինակում է 1998-ի ստանդարտի կապող ու փաթաթող ֆունկցիաներին, С++11-ում այդ ֆունկցիաները անվանված են հնացած (deprecated)։

Ընդհանուր հասկացություններ

Ստանդարտի տերմինները 
C++11 լեզվում օգտագործվում են <functional> վերնագրային ֆայլին վերաբերվող հետևյալ տերմինները՝
 Ֆունկցիայի օբյեկտի տեսակ (function object type)՝ փոստիֆիկ արտահայտություն ֆունկցիա կանչի ժամանակ, որտեղ փոստիֆիկ արտահայտությունը գերբեռնված ֆունկցիաների կամ կաղապարների կամ հասցեների հավաքածու է։
 Զանգի ձև (call signature) այս անվանումից հետո դրվում է Կլոր փակագծեր, որի մեջ գրվում է զրոների հերթականություն։
 Կանչված տեսակ (callable type)՝ ֆունկցիոնալ տիպի օբյեկտ կամ դասի անդամի ցուցիչ։
 Կանչված օբյեկտ (callable object) ՝ կանչված տեսակի օբյեկտ։
 Կանչի օբյեկտի տեսակ (call wrapper type) ` տիպ է, որը պարունակում է կանչվող օբյեկտ և աջակցում է կանչի գործողությանը, որը տանում է պահպանված օբյեկտի կանչին (invoke)։
 Կանչի օբյեկտ (call wrapper)՝ փաթաթող կանչ տեսակի օբյեկտ։
 Նպատկի օբյեկտ (target object)՝ կանչված օբյեկտ, որը պարունակում է կանչի օբյեկտ։

Ֆունկցիոնալ օբյեկտի հասկացություն 
Ֆունկցիոնալ օբյեկտը դաս է որոշակի կանչի ֆունկցիայի օպերատորով՝ operator ()։ Հետևյալ կոդի մեջ
FunctionObjectType func;
func();
func() արտահայտությունը համարվում է func ֆունկցիոնալ օբյեկտի կանչ, այլ ոչ թե որևէ ֆունկիայի կանչ, որի անունը func է։ Ֆունկցիոնալ օբյեկտի ձևը պետք է լինի հետևյալ կերպ՝
class FunctionObjectType {
  public:
  void operator() () {
    // Do some work
  }
};
Մինչև ֆունկցիայի օգտագործումը ֆունկցիոնալ օբյեկտները ունեն մի շարք առավելություններ։ Դրանցից են՝
 Կարող է լինել երկու օբյեկտ նույն ֆունկցիոնալ տեսկի, բայց միևնույն ժամանակ գտնվեն տարբեր իրավիճակներում, որը հնարավոր չէ սովորական ֆունկցիաների ժամանակ։ Նաև ֆունկցիոնալ օբյեկտները կարող են տրամադրել նախնական տվյալների կարգավորման ծրագիր։
 Յուրաքանչյուր ֆունկցիոնալ օբյեկտ ունի իր տեսակը, հետևաբար կա հնարավորություն փոխանցել այդ տեսակը որպես կաղապարի պարամետր, որպեսզի նշի որոշակի վարքագիծ։ Օրինակ՝ կոնտեյների տեսակները տարբեր ֆունկցիոնալ օբեյկներով տարբերվում են։
 Օբյեկտ-ֆունկցիաները ավելի արագ են կատարվում քան ֆունկցիաների ցուցիչները։ Օրինակ՝ կառուցել (inline) դիմումը () դասի օպերատորին ավելի հեշտ քան ֆունկցիայի, որը փոխանցվել է ցուցչով։.

Պրեդիկատներ 
Ֆունկցիոնակ օբյեկտները, որոնք վերադարձնում են բուլյան տեսակ անվանում են պրեդիկատներ։ Ստանդարտ գրադարանում օգտագործում են ունար և բինար պրեդիկատներ։ Պրեդիկատի գործողությունները կախված չէ կատարվող գործողության պատճենման քանակից, քանի որ С++ ստանդարտը չի հաշվում, թե քանի անգամ է պատճենվել ալգորիթմը օգտագործման ժամանակ։

Փաթեթավորող գործառույթներ

std::function 
C++11 ստանդարտից սկսած std::function կաղապարային դասը համարվում է պոլիմորֆիզմ ֆունկցիայի փաթեթ ընդհանուր օգտագործման համար։ std::function դասի օբյեկտները կարող են պահել, պատճենել և կանչել կամայական անհրաժեշտ օբյեկտներ ՝ ֆունկցիա, լյամբդա-արտահայտություն և այլ ֆունկցիոնալ օբյեկտներ։ Ընդհանուր ասած, կամայական տեղ, որտեղ պետք է օգտագործել ֆունկցիայի ցուցիչը՝ կանչը չեղարկելու համար կամ հետ կանչի ֆունկցիա ստեղծելու համար օգտագործվում է std::function ֆունկցիան։

Առաջին անգամ տվյալ ֆունկցիան հայտնվել է Function գրադարանում Boost 1.23.0 տարբերակի ժամանակ։ Հետագա զարգացման ժամանակայն այն ներառվել է C++ TR1-ու և С++11-ում։

Դասի որոշումը 
template<class> class function; // undefined
template<class R, class... ArgTypes> class function<R(ArgTypes...)>;
Ստանդարտում կան նաև օժանդակ բաղադրիչներ՝ swap и assign և համեմատության օպերատորներ՝ (== и !=), nullptr-ի հետ միասին։

Օգտագործման օրինակը 
#include <iostream>
#include <functional>

struct A {
    A(int num) : num_(num){}
    void printNumberLetter(char c) const {std::cout << "Number: " << num_  << " Letter: " << c << std::endl;}
    int num_;
};

void printLetter(char c)
{
    std::cout << c << std::endl;
}

struct B {
    void operator() () {std::cout << "B()" << std::endl;}
};

int main()
{
    // Ֆունկցիա
    std::function<void(char)> f_print_Letter = printLetter;
    f_print_Letter('Q');

    // Լյամբդա-արտահայտություն։
    std::function<void()> f_print_Hello = [] () {std::cout << "Hello world!" << std::endl;};
    f_print_Hello();

    // Կապիչ։
    std::function<void()> f_print_Z = std::bind(printLetter, 'Z');
    f_print_Z();

    // Դասի կանչ։
    std::function<void(const A&, char)> f_printA = &A::printNumberLetter;
    A a(10);
    f_printA(a, 'A');

    // Ֆունկցիոնալ օբյեկտ։
    B b;
    std::function<void()> f_B = b;
    f_B();
}
Վերևում գրված կոդի արդյունքը՝
Q
Hello world!
Z
Number: 10 Letter: A
B()

std::bad_functional_call 
Բացառություն bad_functional_call տիպը կանտեսվի function::operator() ֆունցիայի փաթեթում թիրախի բացակայության դեպքում։ bad_functional_call ֆունկցիան եկել է std::exception ֆունկցիայից և նրա մեջ կա վիրտուալ what() տարբերակը տեքստի սխալը գտնելու համար։ Օգտագործման օրինակը՝
#include <iostream>
#include <functional>

int main()
{
    std::function<void()> func = nullptr;
    try {
        func();
    } catch(const std::bad_function_call& e) {
        std::cout << e.what() << std::endl;
    }
}

std::mem_fn 
std::mem_fn կաղապարային ֆունկցիան ստեղծում է փաթեթող օբյեկտ դասի անդամների շուրջը։ Այդ օբյեկտը կարող է պահել, պատճենել և կանչել դասի անդամին։.

Առաջին անգամ std::mem_fn կաղապարային ֆունկցիան հայտնվել է Member Function գրադարանում (Boost 1.25.0 տարբերակով)։ Այն նույնպես ներառվել է C++ TR1-ում և С++11-ում։ Boost գրադարանում այն կատարելագործվում էր, որպես std::mem_fun и std::mem_fun_ref ստանդարտ ֆունկցիաների ընդհանրացում։

Ֆունկցիոնալ օբյեկտներ 
Նախօրոք որոշված հիմնական հոդվածների համար նախատեսված ֆունկցիոնալ օբյեկտների հավաքածուն, կաղապարների ստանդարտ գրադարանի անբաժան մասն էր կազմում։ Նրանց մեջ մտնում էին թվաբանական գոծողություններ (+-*/%), հիմնական տրամաբանական գործողությունները (&&, ||, !) և համեմատման գործողությունները (==, !=, >, <, >=, <=)։ Տրամաբանական և համեմատության գործողությունները համարվում են հաստատուններ և վերադարձնում են բուլյան տեսակը։ С++11-ից սկսած ավելացել են որոշ բիտային գործողություններ (and, or, xor, not)։

Փաթեթների հղումներ 
<functional> վերնագրային ֆայլում կա օգնող std::reference_wrapper դաս, որն ինքնըստինքյան տալիս է օբյեկտի հղում կամ ֆունկցիայի հղում, որը փոխանցվել էր նրան կաղապարով։ reference_wrapper ֆունկցիան իմաստը կայնում է նրանում, որ պահպանել հղումները, որը փոխանցվել էր T տեսակի կաղապներում և արտածել այն երբ դիմում ենք operator T& ()-ին։

Առաջին անգամ reference_wrapper կաղապարային դասը հայտնվել է Ref գրադարանում (Boost 1.25.0 տարբերակի մեջ)։ որոշ բերելավումներից հետո նա ավելացվել է С++11 մեջ։

reference_wrapper օբյեկտներ ստեղծելու համար տրված են օգնական ref և cref ֆունկցիաներ։ Որոշված են հետևյալ կերպ՝
template <class T> reference_wrapper<T> ref(T& t) noexcept;
template <class T> reference_wrapper<const T> cref(const T& t) noexcept;

Տես նաև 
 C++
 C++11
 C++ լեզվի ստանդարտ գրադարան

Ծանոթագրություններ 

1998 հիմնադրումներ