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

ՊատմությունԽմբագրել

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

Մինչև ֆունկցիայի օգտագործումը ֆունկցիոնալ օբյեկտները ունեն մի շարք առավելություններ[5]։ Դրանցից են՝

  1. Կարող է լինել երկու օբյեկտ նույն ֆունկցիոնալ տեսկի, բայց միևնույն ժամանակ գտնվեն տարբեր իրավիճակներում, որը հնարավոր չէ սովորական ֆունկցիաների ժամանակ։ Նաև ֆունկցիոնալ օբյեկտները կարող են տրամադրել նախնական տվյալների կարգավորման ծրագիր։
  2. Յուրաքանչյուր ֆունկցիոնալ օբյեկտ ունի իր տեսակը, հետևաբար կա հնարավորություն փոխանցել այդ տեսակը որպես կաղապարի պարամետր, որպեսզի նշի որոշակի վարքագիծ։ Օրինակ՝ կոնտեյների տեսակները տարբեր ֆունկցիոնալ օբեյկներով տարբերվում են։
  3. Օբյեկտ-ֆունկցիաները ավելի արագ են կատարվում քան ֆունկցիաների ցուցիչները։ Օրինակ՝ կառուցել (inline) դիմումը () դասի օպերատորին ավելի հեշտ քան ֆունկցիայի, որը փոխանցվել է ցուցչով[6]։.

ՊրեդիկատներԽմբագրել

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

Փաթեթավորող գործառույթներԽմբագրել

std::functionԽմբագրել

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

Առաջին անգամ տվյալ ֆունկցիան հայտնվել է Function գրադարանում Boost 1.23.0 տարբերակի ժամանակ[7]։ Հետագա զարգացման ժամանակայն այն ներառվել է 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 կաղապարային ֆունկցիան ստեղծում է փաթեթող օբյեկտ դասի անդամների շուրջը։ Այդ օբյեկտը կարող է պահել, պատճենել և կանչել դասի անդամին[8]։.

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

Ֆունկցիոնալ օբյեկտներԽմբագրել

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

տեսակ անվանում գործողությունների քանակ վերադարձնող տեսակ գործողություն
Համեմատում equal_to Բինար bool x == y
not_equal_to Բինար bool x != y
greater Բինար bool x > y
less Բինար bool x < y
greater_equal Բինար bool x >= y
less_equal Բինար bool x <= y
Логические logical_and Բինար bool x && y
logical_or Բինար bool x || y
logical_not Ունար bool !x
Թվաբանական plus Բինար T x + y
minus Բինար T x - y
multiplies Բինար T x * y
divides Բինար T x / y
modulus Բինար T x % y
negate Ունար T -x
Բիտային (C++11) bit_and Բինար T x & y
bit_or Բինար T x | y
bit_xor Բինար T x ^ y
bit_not Ունար T ~x

Փաթեթների հղումներԽմբագրել

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

Առաջին անգամ reference_wrapper կաղապարային դասը հայտնվել է Ref գրադարանում (Boost 1.25.0 տարբերակի մեջ)[7]։ որոշ բերելավումներից հետո նա ավելացվել է С++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;

Տես նաևԽմբագրել

ԾանոթագրություններԽմբագրել

  1. «Programming languages - C++» (անգլերեն)։ ISO/IEC 14882։ 1998-04-23։ Արխիվացված է օրիգինալից 2013-05-17-ին։ Վերցված է 2013-05-01 
  2. 2,0 2,1 Alexander Stepanov and Meng Lee (1995-11-14)։ «The Standard Template Library» (անգլերեն)։ HP Laboratories Technical Report 95-11(R.1)։ Արխիվացված օրիգինալից 2013-05-17-ին։ Վերցված է 2013-05-01 
  3. (2005-06-24)։ «Draft Technical Report on C++ Library Extensions»։ ISO/IEC JTC1/SC22/WG21։ Վերցված՝ 2013-05-01։
  4. 4,0 4,1 «ISO/IEC 14882:2011»։ ISO։ 2011-09-2։ Արխիվացված օրիգինալից 2013-05-17-ին։ Վերցված է 2013-05-02 
  5. Josuttis Nicolai M. (2012)։ The C++ standard library : a tutorial and reference։ Addison-Wesley։ ISBN 0-321-62321-5 
  6. Stroustrup Bjarne (2000)։ The C++ Programming Language: Special Edition։ Addison-Wesley։ ISBN 0-201-70073-5 
  7. 7,0 7,1 7,2 «Boost Library Documentation» (անգլերեն)։ Արխիվացված օրիգինալից 2013-05-17-ին։ Վերցված է 2013-05-01 
  8. «Boost Library Documentation : mem_fn.hpp» (անգլերեն)։ Արխիվացված օրիգինալից 2013-05-17-ին։ Վերցված է 2013-05-02