top of page

C++ Dilinde std::invoke

  • Writer: Yusuf Hançar
    Yusuf Hançar
  • Sep 18
  • 9 min read

C++17 ile birlikte dile eklenen std::invoke, farklı türde çağrılabilir nesneleri (callable) tek bir ortak arabirim üzerinden çalıştırabilmemizi sağlayan küçük ama güçlü bir adaptördür. Normalde bir fonksiyonu doğrudan ismiyle çağırabiliriz, bir fonksiyon pointer’ını çalıştırabiliriz ya da bir üye fonksiyon göstergesi üzerinden çağrı yapabiliriz. Ancak her durumda sözdizimi biraz farklılık gösterir. İşte std::invoke, bu farklılıkları ortadan kaldırarak tek tip ve güvenli bir çağrı mekanizması sunar.

Özellikle üye fonksiyon göstericilerinde (member function pointer) sıkça kullanılan std::invoke, çağrılacak hedefin serbest fonksiyon mu, üye fonksiyon mu, yoksa functor/lamda mı olduğuna bakmaksızın doğru şekilde çağırır. Bu da şablon tabanlı kodlarda, türden bağımsız bir çağrı modeli kurmamızı kolaylaştırır.

Kısacası std::invoke, “nasıl çağıracağım?” sorusunu ortadan kaldırır ve elimizdeki callable nesneyi parametreleriyle birlikte çalıştırır. Bu sayede hem kod okunabilirliği artar hem de çağrı biçimlerinin birleştirilmesiyle hataya açık noktalar azalır.


Üye Fonksiyon Göstericiler ve Sınıfların non-static veri elemanlarını gösteren göstericiler

  • Serbest fonksiyonlar (yani sınıfın dışında tanımlanan normal fonksiyonlar) için gösterici tanımı

int foo(int, int)
{
    return 1;
}

int main() {
    // foo’yu gösteren pointer
    int (*fp)(int, int) = foo;  

    int result = fp(3, 5);   // foo(3,5) ile aynı
}
*****************************              
AÇIKLAMA :  Burada fp fonksiyonun adresini tutar.
C++11’den itibaren tür çıkarımı (type deduction) yapabiliriz.
auto fp = foo;        // derleyici fp’nin türünü int(*)(int,int) olarak çıkarır
*****************************             
CEVAP :    
*****************************

int foo(int, int)
{
    return 1;
}

//typedef int (*Fptr)(int, int); // alias ->  int (*)(int, int)
using Fptr = int(*)(int, int); // c++11

int main ()  
{ 
    Fptr fp = foo;
}
class SmartCode { 
public :
    static int foo(int x)
    {
        std::cout << "static int SmartCode::foo(int x)x : " << x << std::endl;
        return x + 5;       
    }
}; 

int main ()  
{ 
    int (*fp)(int) = SmartCode::foo;

    auto val = fp(30);  // auto val = (*fp)(30);               

    std::cout << "val : " << val << endl;
}
*****************************              
AÇIKLAMA :  
*****************************             
CEVAP :    
static int SmartCode::foo(int x)x : 30
val : 35
*****************************

non static üye fonksiyonlar için member function pointer tanımı
class SmartCode {
public :
    int foo(int val)                               // bunun this pointer 'ı vardır.
    {
        std::cout << "int SmartCode::foo(int val) val : " << val << " this pointer : " << this <<  std::endl;

        return val * val;
    }
};

int main ()
{
    int(*fptr)(int) = SmartCode::foo;
}
*****************************              
AÇIKLAMA :  2 tane sorun var.
            1- non-static üye fonksiyonun ismi otomatik olarak adrese dönüşmüyor.    
            int (*fptr)(int) = &Myclass::foo;
            
            2- tür " int(*fptr)(int) " 
               bu değil. Bu global ya da static üye fonksiyona ait bir adres.
            " int(SmartCode::*fptr)(int) = &SmartCode::foo; "   
              şeklinde olmalıdır. 
              buradaki fptr gibi fonksiyonlara "member function pointer" denir
*****************************             
CEVAP :  main.cpp:16:32: error: invalid use of non-static member function ‘int SmartCode::foo(int)’
*****************************
class SmartCode { 
public : 
    int foo(int val)                            
    { 
        std::cout << "int SmartCode::foo(int val) val : " << val << " this pointer : " << this <<  std::endl; 
        return val * val; 
    } 
}; 

int main () 
{ 
    //int(SmartCode::*fptr)(int) = &Myclass::foo;       // bunun yerine modern c++ ile 
    auto fptr = &SmartCode::foo;                       
}
class SmartCode { 
public : 
    int foo(int val)                             
    { 
        std::cout << "int SmartCode::foo(int val) val : " 
                  << val << " this pointer : " << this << std::endl; 

        return val * val; 
    } 
}; 

using Mfptr = int(SmartCode::*)(int); 

int main () 
{
    Mfptr fp = &SmartCode::foo; 
    
    fp(12);  
}
*****************************              
AÇIKLAMA :  this pointer'ı nerde, nerede nesne ?
*****************************             
CEVAP : error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘fp (...)’, e.g. ‘(... ->* fp) (...)’
*****************************
class SmartCode { 
public : 
    int foo(int val)                             
    { 
        std::cout << "int SmartCode::foo(int val) val : " 
                  << val << " / this pointer : " << this << std::endl; 

        return val * val; 
    } 
}; 

using Mfptr = int(SmartCode::*)(int); 

int main () 
{ 
    SmartCode mx;
    
    std::cout << "&mx : " << &mx << std::endl;

    auto fptr = &SmartCode::foo;
    auto val  = (mx.*fptr)(12);

    std::cout << "val : " << val << std::endl;
}
*****************************              
AÇIKLAMA :  
üye fonksiyon göstericileri ile fonksiyon çağrısı yapmak için kullanılan operatorler. 
.* operatoru ile
*****************************             
CEVAP :  
&mx : 0x7fffeb2382fb
int SmartCode::foo(int val) val : 12 / this pointer : 0x7fffeb2382fb
val : 144
*****************************

  • .* ve ->*

    bunlar overload edilemeyen operatorlerdir ve C de yoktur.


class SmartCode { 
public : 
    int foo(int val)                             
    { 
        std::cout << "int SmartCode::foo(int val) val : " 
                  << val << " / this pointer : " << this << std::endl;  

        return val * val; 
    } 
}; 

using Mfptr = int(SmartCode::*)(int);

int main () 
{ 
    auto pd = new SmartCode;

    std::cout << "pd : " << pd << std::endl;

    auto fp = &SmartCode::foo;

    auto val = (pd->*fp)(45);
    std::cout << "val : " << val << std::endl;
} 
*****************************              
AÇIKLAMA :  
*****************************             
CEVAP : 
pd : 0x600e98aac2b0
int SmartCode::foo(int val) val : 45 / this pointer : 0x600e98aac2b0
val : 2025
*****************************

std::invoke

Parameters

f - Callable object to be invoked

args - arguments to pass to f


Return value

1) The value returned by f.

2) The value returned by f, implicitly converted to R, if R is not (possibly cv-qualified) void. None otherwise.


Exceptions

1) noexcept specification:

    noexcept(std::is_nothrow_invocable_v<F, Args...>)

2) noexcept specification:

    noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>)


template< class F, class... Args >
std::invoke_result_t<F, Args...>
    invoke( F&& f, Args&&... args ) noexcept(/* see below */); // c++20

template< class R, class F, class... Args >
constexpr R
    invoke_r( F&& f, Args&&... args ) noexcept(/* see below */); // c++23

#include <functional>

void func (int x)
{
    std::cout << "func cagirildi :  " << x << std::endl;
}

int main () 
{ 
    func(12);
    std::invoke(func, 15);
} 
*****************************             
CEVAP :   
func cagirildi :  12
func cagirildi :  15
*****************************
int func(int x, int y)
{
    return x * y + 4;
}

int main()
{
    auto val = std::invoke(func, 4, 8);

    std::cout << val;
}
*****************************   
AÇIKLAMA :
std::invoke perfect forwarding mekanizmasını kullanarak func fonksiyonunu 4 ve 8 değerleri ile çağırır.
*****************************   
CEVAP : 36
*****************************
#include <functional> 

void func (int x) 
{ 
    std::cout << "func cagirildi :  " << x << std::endl; 
} 

int main () 
{ 
    auto fp = &func;

    std::invoke(fp, 20);
    fp(30);                 
} 
class SmartCode {
public :
    int operator ()(int x)const
    {
        return x * x + 5;
    }
};

int main () 
{ 
    SmartCode sc;
    
    std::cout << sc(12) << "\n";
    std::cout << std::invoke(sc, 12);
} 
class SmartCode { 
public : 
    SmartCode (int val) : mx { val } {} 

    int operator()(int x)const 
    { 
        return x * mx; 
    } 
};

int main () 
{ 
    std::cout << std::invoke(SmartCod{ 20 }, 15); 
} 
int main () 
{ 
    int ival{ 10 }; 
    
    auto fn = [ival](int x) {return x * ival; };
    
    std::cout << std::invoke(fn, 20);
} 
class SmartCode {
public :
    int func(int x)
    {
        return x + 10;
    }
};

int main()
{
    SmartCode sc;

    auto val = std::invoke(&SmartCode::func, sc, 10);

    std::cout << val;
}
*****************************   
CEVAP : 20
*****************************
class SmartCode {
public :
    int func(int x)
    {
        return x + 10;
    }
};

int main()
{
    int (*fp)(int) = &SmartCode::func;
}
*****************************   
AÇIKLAMA : SmartCode sınıfının func fonksiyonunun adresini bir function pointer da tutar mıyız ?
int (*fp)(int) yerine auto fp yazılabilir ancak pekiştirmek için yazıyoruz.
*****************************   
CEVAP : error: cannot convert ‘int (SmartCode::*)(int)’ to ‘int (*)(int)’ in initialization
*****************************
int main()
{
    int (SmartCode::*fp)(int) = &SmartCode::func;  // legal
}
int main()
{
    int (SmartCode::*fp)(int) = &SmartCode::func; 

    // çağırılabilmesi için bir SmartCode sınıfı nesnesine ihtiyaç vardır.
    fp(20);   // error
}
int main()
{
    int (SmartCode::*fp)(int) = &SmartCode::func;  // legal

    SmartCode sc;

    sc.*fp(10);    // şeklinde yazabiliriz ancak operator önceliği problemi vardır             
}
*****************************   
AÇIKLAMA : .* operatoru ile çağrı yaptık.

.* operatorunun önceliği fonksiyon çağrı operatorunun önceliğinden düşük olduğundan sc nesnesini member function pointer ile kullanarak fonksiyonu çağırabilmek için öncelik parantezine ihtiyaç vardır.
***************************** 
int main()
{
    int (SmartCode::*fp)(int) = &SmartCode::func; 

    SmartCode sc;

    (sc.*fp)(10);    
}
int main()
{
    auto fp = &SmartCode::func;  

    SmartCode* ptr = new SmartCode;  // dynamic allocation

    ((*ptr).*fp)(10);

    delete ptr;   
}
*****************************   
AÇIKLAMA : .* operatorunu (sc.*fp)(10) şeklinde çağırmak hem karmaşıklık oluşturur hem de generic programlama için uygun değildir.
((*ptr).*fp)(10); artık bu şekilde yazma karmaşıklığına gerek yoktur. std::invoke burda devreye girebilir.
*****************************   
CEVAP : legal
*****************************
int main()
{
    auto fp = &SmartCode::func;  

    SmartCode sc;

    auto val = std::invoke(fp, sc, 5);     
}
int main()
{
    auto fp = &SmartCode::func;  

    SmartCode* ptr = new SmartCode; 

    // dereference etmeden doğrudan nesnenin adresini geçtik.
    auto val = std::invoke(fp, ptr, 5);     

    delete ptr;   
}
class SmartCode {
public :
    static int func(int x)
    {
        return x + 10;
    }
};

int main()
{
    int (*fp)(int) = &SmartCode::func;
}
*****************************   
AÇIKLAMA : static func call
*****************************   
CEVAP : legal
*****************************
class SmartCode {
public :
    int x{ 10 };
    int y{ 15 };
};

int main()
{
    SmartCode sc;
    int SmartCode::*ptr = &SmartCode::x;

    std::cout << sc.x << "\n";
    std::invoke(ptr, sc) = 20;
    std::cout << sc.x << "\n";
}
*****************************   
AÇIKLAMA :member function pointer da kullanıldığı gibi data member function kullanırken de std::invoke kullanılabilir.
*****************************   
CEVAP :
10
20
*****************************
class SmartCode { 
    int mx = 0;
public : 
    SmartCode() = default;

    SmartCode(int val) : mx { val } {} 

    int foo(int x, int y)
    {
        std::cout << "SmartCode::foo(int, int)" << std::endl;

        return mx * (x + y);
    }
}; 

int main () 
{ 
     SmartCode sc{ 17 };
    
    auto fp = &SmartCode::foo;

    std::cout << std::invoke(fp, sc, 5, 15);
}  
*****************************             
CEVAP :   SmartCode::foo(int, int)
          340
*****************************
class SmartCode {
    int mx = 0;
public:
    SmartCode(int val) : mx{val} {}
    
    int f1(int x, int) { std::cout << "f1 : "; return mx * x; } 
    
    int f2(int x, int y){ std::cout << "f2 : "; return mx * x * 2; }
    
    int f3(int x, int y){ std::cout << "f3 : "; return mx * x * 3; }
    
    int f4(int x, int y){ std::cout << "f4 : "; return mx * x * 4; }
};

using Mfptr2 = int (SmartCode::*)(int, int);

int main() 
{
    Mfptr2 arr[] = { &SmartCode::f1, &SmartCode::f2, &SmartCode::f3, &SmartCode::f4 };
    SmartCode mx{10};

    for (auto fn : arr) 
    {
        std::cout << std::invoke(fn, mx, 10, 0) << '\n'; 
    }
}
*****************************              
AÇIKLAMA :  
*****************************             
CEVAP :  
f1 : 100
f2 : 200
f3 : 300
f4 : 400
*****************************

int main () 
{ 
    Sfptr arr[] = { &SmartCode::f1, &SmartCode::f2, &SmartCode::f3, &SmartCode::f4 }; 
    SmartCode sc { 10 }; 

    auto val = std::invoke(arr[2], sc, 10);
    //auto val = (sc.*arr[2])(10);
}
*****************************              
AÇIKLAMA :  aynı kullanım
*****************************  

class SmartCode {  
public : 
    int func1(string, int, int);
    int func2(string, int, int);
    int func3(string, int, int);
    int func4(string, int, int);

    void foo(int, int(SmartCode::*fptr)(string, int, int)
    {
        auto val = std::invoke(*fptr, *this, "smartcode", 5, 6);
    }
}; 

#include <iostream>
#include <string>
#include <functional>

class SmartCode {
public:
    int func1(std::string, int, int);
    int func2(std::string, int, int);
    int func3(std::string, int, int);
    int func4(std::string, int, int);

    void foo(int /*unused*/,
             int (SmartCode::*fptr)(std::string, int, int))
    {
        auto val = std::invoke(fptr, *this, "smartcode", 5, 6);
        std::cout << "val = " << val << '\n';
    }
};
*****************************              
AÇIKLAMA :  
int (SmartCode::*fptr)(std::string, int, int) → SmartCode sınıfının üye fonksiyon pointer türüdür. Parametre imzası bire bir uymalıdır.

foo içinde std::invoke(fptr, *this, ...) kullanıldığında:

fptr üye fonksiyon pointer’ı,

*this çağırılacak nesne,

"smartcode", 5, 6 ise argümanlardır.

std::invoke tüm bu farklı durumları (serbest fonksiyon, üye fonksiyon, data member) tekleştirerek çağırır. 
İçeride aslında (this->*fptr)(...) koduna indirgenir.
*****************************   
class SmartCode {
public:
    int func1(std::string, int, int);
    int func2(std::string, int, int);
    int func3(std::string, int, int);
    int func4(std::string, int, int);

    void foo(int /*unused*/,
             int (SmartCode::*fptr)(std::string, int, int))
    {
        auto val = (this->*fptr)("smartcode", 5, 6);
        std::cout << "val = " << val << '\n';
    }
};
*****************************              
AÇIKLAMA :  
std::invoke yerine, doğrudan özel sözdizimiyle çağrı yapılıyor.

(this->*fptr)(...) → “bu nesne (this) üzerinde, fptr’ın gösterdiği üye fonksiyonu çağır” anlamına gelir.

Eğer SmartCode* p = this; olsaydı çağrı (p->*fptr)(...) şeklinde yapılırdı.

.* ve ->* operatörleri yalnızca pointer-to-member fonksiyonlar (ve data member’lar) için geçerlidir.
***************************** 

class SmartCode {
public:
    int funcConst(std::string s, int x, int y) const 
    {
        std::cout << "funcConst: " << s << " " << x << " " << y << "\n";

        return x + y;
    }

    using ConstMemFun = int (SmartCode::*)(std::string, int, int) const;

    void fooConst(ConstMemFun fptr) const 
    {
        auto v1 = std::invoke(fptr, *this, "smartcode", 7, 8);

        auto v2 = (this->*fptr)("smartcode", 7, 8);

        std::cout << "v1=" << v1 << " v2=" << v2 << '\n';
    }
};
*****************************              
AÇIKLAMA :  
int funcConst(...) const; → Bu fonksiyon nesnenin durumunu değiştirmez; const niteleyicisi imzanın parçasıdır.

Dolayısıyla pointer türü : int (SmartCode::*)(std::string,int,int) ile aynı değildir.

Çağrı yapılırken hem std::invoke hem de (this->*fptr)(...) çalışır, ama fooConst’un kendisi de const olmalıdır; çünkü this artık const SmartCode*
*****************************  
struct SmartCode {
    int mx = 10, my = 20;
};

int main () 
{ 
    SmartCode sc; 
    //int SmartCode::* ptr = &SmartCode::mx; 
    auto ptr =  &SmartCode::mx; 
     
    std::cout << sc.*ptr << std::endl;   
    sc.*ptr = 25; 
    std::cout << sc.mx; 
}
*****************************              
AÇIKLAMA :  
ptr nin gösterdiği nesneye erişmekten bahsetmek için SmartCode nesnesi gerekir.
*****************************             
CEVAP :    10
           25
*****************************
struct SmartCode { 
    int mx = 10, my = 20; 
};

int main () 
{ 
    SmartCode sc; 
    auto ptr = &SmartCode::mx; 

    std::cout << std::invoke(ptr, sc); 
}  
*****************************              
CEVAP : 10 
*****************************
#include <iostream>
#include <functional> 
#include <string> 
#include <format>

// Invoking typical callables (i.e. lambdas, functions...)
auto zero_arg = [](){ return 42; };
auto two_arg = [](int a, int b) { return a + b; };

int a = std::invoke(zero_arg);       // a == 42
int b = std::invoke(two_arg, 98, 2); // b == 100

// Demonstration of member invocation
struct SmartCode {
    int val;
    int get_val() { return val; }
    int add(int ex) { return val + ex; }
};

SmartCode sc{42};

int c = std::invoke(&SmartCode::val, sc);     // c == 42
int d = std::invoke(&SmartCode::get_val, sc); // d == 42
int e = std::invoke(&SmartCode::add, sc, 42); // e == 84

// Demonstration of using std::invoke as an alternative
// to std::(move_only_)function. // c++23

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

// Customizing code with a std::(move_only_)function.
void operate(int a, int b, 
             std::move_only_function<void(std::string)> lg) 
{
    int c = func(a,b);
    
    lg(std::format("func called with {} and {},"
                       " the result was {}.", a, b, c));
}

// Statically customizing code with a callable.
void operate_static(int a, int b, auto&& lg) 
{
    int c = func(a,b);
    
    std::invoke(std::forward<decltype(lg)>(lg), 
        std::format("func called with {} and {},"
                    " the result was {}.", a, b, c));
}

int main()
{
    auto sc1 = [](std::string str) { 
      std::cout << "sc1 : " << str << "\n"; 
    };
    
    auto sc2 = [](std::string str) {
      std::cout << "sc2 : " << str << "\n";
    };
    
    // Two different callables, type erased:
    operate(1, 2, sc1);
    operate(1, 2, sc2);
    
    // Two different callables, two generated functions:
    operate_static(1, 2, sc1);
    operate_static(1, 2, sc2);
}
*****************************              
AÇIKLAMA :  c++23 ile genişletildi.
std::function veya std::move_only_function'ın tür silme özelliklerine ihtiyacımız yoksa, std::invoke daha düşük seviyeli bir alternatif olabilir (çağrılabilir ve argümanlar statik olarak türetilir).
*****************************             

 
 
 

Recent Posts

See All
C++ Dilinde [[nodiscard]] attribute

C++’ta [[attribute]] (öznitelik) mekanizması, kod hakkında ek bilgi sağlayarak derleyicinin uyarılar vermesine, belirli optimizasyonlar...

 
 
 

Comments


bottom of page