Functions in c++

Function Pointer

using namespace std;

int fun(){
    cout<<"call fun";
    return 0;
}
typedef int (*ff)();
using fu=ff;

int main(int _argc,char* _argv[]){
    int(*f)()=fun;
    f();
    (*f)();
    ff fff=(ff)fun;
    fu fu1=fun;
    fu1();
}
class A{
public:
    virtual int fun(){
        cout<<"A";
        return 0;
    }
};

class B:public A{
public:
    int fun() override {
        cout<<"B";
        return 0;
    }
};

typedef int(A::*f)();

int main(int _argc,char* _argv[]){
    f ff=&A::fun;
    A a{};
    B b{};
    (a.*ff)();//A
    (b.*ff)();//B
}
class A{
public:
    int fun(){
        cout<<"A";
        return 0;
    }
};

class B:public A{
public:
    int fun() {
        cout<<"B";
        return 0;
    }
};

typedef int(A::*f)();

int main(int _argc,char* _argv[]){
    f ff=f(&B::fun);
    A a{};
    B b{};
    (a.*ff)();//B
    (b.*ff)();//B
}

you can not cast non virtual function pointer

class Foo{
public:
  int f(char* c=0){
    std::cout<<"Foo::f()"<<std::endl;
    return 1;
  }
};
 
class Bar{
public:
  void b(int i=0){
    std::cout<<"Bar::b()"<<std::endl;
  }
};
 
class FooDerived:public Foo{
public:
  int f(char* c=0){
    std::cout<<"FooDerived::f()"<<std::endl;
    return 1;
  }
};
 
int main(int argc, char* argv[]){
  typedef  int (Foo::*FPTR) (char*);
  typedef  void (Bar::*BPTR) (int);
  typedef  int (FooDerived::*FDPTR) (char*);
 
  FPTR fptr = &Foo::f;
  BPTR bptr = &Bar::b;
  FDPTR fdptr = &FooDerived::f;
 
  //Bptr = static_cast<void (Bar::*) (int)> (fptr); //error
  fdptr = static_cast<int (Foo::*) (char*)> (fptr); //OK: contravariance
 
  Bar obj;
  ( obj.*(BPTR) fptr )(1);//call: Foo::f()
}
Output:
Foo::f()

you can cast virtual function pointer

class Foo{
public:
  virtual int f(char* c=0){
    std::cout<<"Foo::f()"<<std::endl;
    return 1;
  }
};
 
class Bar{
public:
  virtual void b(int i=0){
    std::cout<<"Bar::b()"<<std::endl;
  }
};
 
class FooDerived:public Foo{
public:
  int f(char* c=0){
    std::cout<<"FooDerived::f()"<<std::endl;
    return 1;
  }
};
 
int main(int argc, char* argv[]){
  typedef  int (Foo::*FPTR) (char*);
  typedef  void (Bar::*BPTR) (int);
  FPTR fptr=&Foo::f;
  BPTR bptr=&Bar::b;
 
  FooDerived objDer;
  (objDer.*fptr)(0);//call: FooDerived::f(), not Foo::f()
 
  Bar obj;
  ( obj.*(BPTR) fptr )(1);//call: Bar::b() , not Foo::f()
}
Output:
FooDerived::f()
Bar::b()
class A{
public:
    void fun(){
        cout<<"A";
    }
};
class B:public A{
public:
    void fun(){
        cout<<"B";
    }
};
int main(int _argc,char* _argv[]){
    typedef void (A::*f)();
    f f1=(f)&B::fun;
    B b;
    (b.*f1)();// if B did not inherit A (b.*(void (B::*)())f1)();
}

generic function pointer

template<class T>
void fun(void(T::*f)(int)){
    T t;
    (t.*f)(12);
}

class A{
    public:
        void fun(int i){
            cout<<i;
        }
    };

int main(int argc,char* argv[]){
    fun(&A::fun);
    return 0;
}
template<int i,typename T>
 int iter(T t){
     return i-1;
 }

 template<typename T>
 struct A{
     T t;
 };

 template<typename T>
 using fun= int(*)(T t);

 template <typename T>
 using attr=T A<T>::*;


int main(int argc, char *argv[])
{
    QCoreApplication a(argc,argv);
    fun<int> f=iter<2,int>;
    attr<int> at=&A<int>::t;

    return a.exec();
}

lambda expression

[ captures ] ( params ) -> ret { body }
[ captures ] ( params ) { body }
[ captures ] { body }

[&]{};          // OK: by-reference capture default
[&, i]{};       // OK: by-reference capture, except i is captured by copy
[&, &i] {};     // Error: by-reference capture when by-reference is the default
[&, this] {};   // OK, equivalent to [&]
[&, this, i]{}; // OK, equivalent to [&, i]
[=]{};          // OK: by-copy capture default
[=, &i]{};      // OK: by-copy capture, except i is captured by reference
[=, *this]{};   // until C++17: Error: invalid syntax
                    // since c++17: OK: captures the enclosing S2 by copy
[=, this] {};   // until C++20: Error: this when = is the default
                    // since C++20: OK, same as [=]
auto fun=[](int i)->int {return i;};
std::function<int(int)> fun2=fun;
int(*fun3)(int)=fun;
cout<<fun2(4);
class S {
  int x = 0;
  void f() {
    int i = 0;
//  auto l1 = [i, x]{ use(i, x); };    // error: x is not a variable
    auto l2 = [i, x=x]{ use(i, x); };  // OK, copy capture
    i = 1; x = 1; l2(); // calls use(0,0)
    auto l3 = [i, &x=x]{ use(i, x); }; // OK, reference capture
    i = 2; x = 2; l3(); // calls use(1,2)
  }
};
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
 
int main()
{
    std::vector<int> c = {1, 2, 3, 4, 5, 6, 7};
    int x = 5;
    c.erase(std::remove_if(c.begin(), c.end(), [x](int n) { return n < x; }), c.end());
 
    std::cout << "c: ";
    std::for_each(c.begin(), c.end(), [](int i){ std::cout << i << ' '; });
    std::cout << '\n';
 
    // the type of a closure cannot be named, but can be inferred with auto
    // since C++14, lambda could own default arguments
    auto func1 = [](int i = 6) { return i + 4; };
    std::cout << "func1: " << func1() << '\n';
 
    // like all callable objects, closures can be captured in std::function
    // (this may incur unnecessary overhead)
    std::function<int(int)> func2 = [](int i) { return i + 4; };
    std::cout << "func2: " << func2(6) << '\n';
}

Function Declaration

Return type deduction
int x = 1;
auto f() { return x; }        // return type is int
const auto& f() { return x; } // return type is const int&
auto f(bool val)
{
    if (val) return 123; // deduces return type int
    else return 3.14f;   // error: deduces return type float
}
auto f() {}              // returns void
auto g() { return f(); } // returns void
auto* x() {}             // error: cannot deduce auto* from void
struct F
{
    virtual auto f() { return 2; } // error virtual function cannot return type deduction
};

declarations examples

int a = 1, *p = NULL, f(), (*pf)(double);
// decl-specifier-seq is int
// declarator f() declares (but doesn't define)
//                a function taking no arguments and returning int
 
struct S
{
    virtual int f(char) const, g(int) &&; // declares two non-static member functions
    virtual int f(char), x; // compile-time error: virtual (in decl-specifier-seq)
                            // is only allowed in declarations of non-static
                            // member functions
};
int f(int a, int *p, int (*(*x)(double))[3]);
int f(int a = 7, int *p = nullptr, int (*(*x)(double))[3] = nullptr);
int f(int, int *, int (*(*)(double))[3]);
int f(int = 7, int * = nullptr, int (*(*)(double))[3] = nullptr);

Default arguments

int fun0(){
    std::vector<int> v{1,2,3,4};
    return v.size();
}
void fun(int i=fun0()){

}

Argument-dependent lookup

int main()
{
    std::cout << "Test\n"; // There is no operator<< in global namespace, but ADL
                           // examines std namespace because the left argument is in
                           // std and finds std::operator<<(std::ostream&, const char*)
    operator<<(std::cout, "Test\n"); // same, using function call notation
 
    // however,
    std::cout << endl; // Error: 'endl' is not declared in this namespace.
                       // This is not a function call to endl(), so ADL does not apply
 
    endl(std::cout); // OK: this is a function call: ADL examines std namespace
                     // because the argument of endl is in std, and finds std::endl
 
    (endl)(std::cout); // Error: 'endl' is not declared in this namespace.
                       // The sub-expression (endl) is not a function call expression
}
using std::swap;
swap(obj1, obj2);
namespace A {
      struct X;
      struct Y;
      void f(int);
      void g(X);
}
 
namespace B {
    void f(int i) {
        f(i);   // calls B::f (endless recursion)
    }
    void g(A::X x) {
        g(x);   // Error: ambiguous between B::g (ordinary lookup)
                //        and A::g (argument-dependent lookup)
    }
    void h(A::Y y) {
        h(y);   // calls B::h (endless recursion): ADL examines the A namespace
                // but finds no A::h, so only B::h from ordinary lookup is used
    }
}

Overload Resolution

struct B { void f(int); };
struct A { operator B&(); };
A a;
a.B::f(1); // Error: user-defined conversions cannot be applied
           // to the implicit object parameter
static_cast<B&>(a).f(1); // OK
int f1(int);
int f2(float);
struct A {
    using fp1 = int(*)(int);
    operator fp1() { return f1; } // conversion function to pointer to function
    using fp2 = int(*)(float);
    operator fp2() { return f2; } // conversion function to pointer to function
} a;
int i = a(1); // calls f1 via pointer returned from conversion function
struct A {
    operator int(); // user-defined conversion
};
A operator+(const A&, const A&); // non-member user-defined operator
void m()
{
    A a, b;
    a + b; // member-candidates: none
           // non-member candidates: operator+(a,b)
           // built-in candidates: int(a) + int(b)
           // overload resolution chooses operator+(a,b)
}
struct Y { operator int*(); };  // Y is convertible to int*
int *a = Y() + 100.0; // error: no operator+ between pointer and double

Viable functions

struct A { A(int); };
struct B { B(A); };
B b{ {0} }; // list-init of B
// candidates: B(const B&), B(B&&), B(A)
// {0} -> B&& not viable: would have to call B(A)
// {0} -> const B&: not viable: would have to bind to rvalue, would have to call B(A)
// {0} -> A viable. Calls A(int): user-defined conversion to A is not banned
template<class T> struct A {
    using value_type = T;
    A(value_type);                  // #1
    A(const A&);                    // #2
    A(T, T, int);                   // #3
    template<class U> A(int, T, U); // #4
};                                 
A x (1, 2, 3);  // uses #3, generated from a non-template constructor
A a (42); // uses #6 to deduce A<int> and #1 to initialize
A b = a;  // uses #5 to deduce A<int> and #2 to initialize
A b2 = a;  // uses #7 to deduce A<A<int>> and #1 to initialize
void Fcn(const int*, short); // overload #1
void Fcn(int*, int); // overload #2
int i;
short s = 0;
void f() 
{
    Fcn(&i, 1L);  // 1st argument: &i -> int* is better than &i -> const int*
                  // 2nd argument: 1L -> short and 1L -> int are equivalent
                  // calls Fcn(int*, int)
 
    Fcn(&i,'c');  // 1st argument: &i -> int* is better than &i -> const int*
                  // 2nd argument: 'c' -> int is better than 'c' -> short
                  // calls Fcn(int*, int)
 
    Fcn(&i, s);   // 1st argument: &i -> int* is better than &i -> const int*
                  // 2nd argument: s -> short is better than s -> int
                  // no winner, compilation error
}

Ranking of implicit conversion sequences

struct Base {};
struct Derived : Base {} d;
int f(Base&);    // overload #1
int f(Derived&); // overload #2
int i = f(d); // d -> Derived& has rank Exact Match
              // d -> Base& has rank Conversion
              // calls f(Derived&)
int i;
int f1();
int g(const int&);  // overload #1
int g(const int&&); // overload #2
int j = g(i);    // lvalue int -> const int& is the only valid conversion
int k = g(f1()); // rvalue int -> const int&& better than rvalue int -> const int&
int f(void(&)());  // overload #1
int f(void(&&)()); // overload #2
void g();
int i1 = f(g);     // calls #1
int f(const int &); // overload #1
int f(int &);       // overload #2 (both references)
int g(const int &); // overload #1
int g(int);         // overload #2
int i;
int j = f(i); // lvalue i -> int& is better than lvalue int -> const int&
              // calls f(int&)
int k = g(i); // lvalue i -> const int& ranks Exact Match
              // lvalue i -> rvalue int ranks Exact Match
              // ambiguous overload: compilation error
int f(const int*);
int f(int*);
int i;
int j = f(&i); // &i -> int* is better than &i -> const int*, calls f(int*)
struct A {
    operator short(); // user-defined conversion function
} a;
int f(int);   // overload #1
int f(float); // overload #2
int i = f(a); // A -> short, followed by short -> int (rank Promotion)
              // A -> short, followed by short -> float (rank Conversion)
              // calls f(int)

Implicit conversion sequence in list-initialization

void f1(int);                                 // #1
void f1(std::initializer_list<long>);         // #2
void g1() { f1({42}); }                       // chooses #2
 
void f2(std::pair<const char*, const char*>); // #3
void f2(std::initializer_list<std::string>);  // #4
void g2() { f2({"foo","bar"}); }              // chooses #4
void f(int    (&&)[] );    // overload #1
void f(double (&&)[] );    // overload #2
void f(int    (&&)[2]);    // overload #3
 
f({1});          // #1: Better than #2 due to conversion, better than #3 due to bounds
f({1.0});        // #2: double -> double is better than double -> int
f({1.0, 2.0});   // #2: double -> double is better than double -> int
f({1, 2});       // #3: -> int[2] is better than -> int[], 
                 //     and int -> int is better than int -> double
struct A { int x, y; };
struct B { int y, x; };
 
void f(A a, int); // #1
void f(B b, ...); // #2
void g(A a); // #3
void g(B b); // #4
 
void h() 
{
    f({.x = 1, .y = 2}, 0); // OK; calls #1
    f({.y = 2, .x = 1}, 0); // error: selects #1, initialization of a fails
                            // due to non-matching member order
    g({.x = 1, .y = 2}); // error: ambiguous between #3 and #4
}
struct A { A(std::initializer_list<int>); };
void f(A);
struct B { B(int, double); };
void g(B);
g({'a','b'});  // calls g(B(int,double)), user-defined conversion
// g({1.0, 1,0}); // error: double->int is narrowing, not allowed in list-init
void f(B);
// f({'a','b'}); // f(A) and f(B) both user-defined conversions
struct A { int m1; double m2;};
void f(A);
f({'a','b'});  // calls f(A(int,double)), user-defined conversion

operator overloading

assignmentincrement
decrement
arithmeticlogicalcomparisonmember
access
other
a = b
a += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b
++a
–a
a++
a–
+a
-a
a + b
a – b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b
!a
a && b
a || b
a == b
a != b
a < b
a > b
a <= b
a >= b
a <=> b
a[b]
*a
&a
a->b
a.b
a->*b
a.*b
a(…)
a, b
? :

static_cast converts one type to another related type
dynamic_cast converts within inheritance hierarchies
const_cast adds or removes cv qualifiers
reinterpret_cast converts type to unrelated type
C-style cast converts one type to another by a mix of static_castconst_cast, and reinterpret_cast
new creates objects with dynamic storage duration
delete destructs objects previously created by the new expression and releases obtained memory area
sizeof queries the size of a type
sizeof… queries the size of a parameter pack (since C++11)
typeid queries the type information of a type
noexcept checks if an expression can throw an exception (since C++11)
alignof queries alignment requirements of a type (since C++11)

(since C++20) && || ++ — , ->* -> ( ) [ ]

std::string str = "Hello, ";
str.operator+=("world");                       // same as str += "world";
operator<<(operator<<(std::cout, str) , '\n'); // same as std::cout << str << '\n';
                                               // (since C++17) except for sequencing

assignment operator

T& operator=(const T& other) // copy assignment
{
    if (this != &other) { // self-assignment check expected
        if (other.size != size) {         // storage cannot be reused
            delete[] mArray;              // destroy storage in this
            size = 0;
            mArray = nullptr;             // preserve invariants in case next line throws
            mArray = new int[other.size]; // create storage in this
            size = other.size;
        } 
        std::copy(other.mArray, other.mArray + other.size, mArray);
    }
    return *this;
}
T& operator=(T&& other) noexcept // move assignment
{
    if(this != &other) { // no-op on self-move-assignment (delete[]/size=0 also ok)
        delete[] mArray;                               // delete this storage
        mArray = std::exchange(other.mArray, nullptr); // leave moved-from in valid state
        size = std::exchange(other.size, 0);
    }
    return *this;
}
T& T::operator=(T arg) noexcept // copy/move constructor is called to construct arg
{
    std::swap(size, arg.size); // resources are exchanged between *this and arg
    std::swap(mArray, arg.mArray);
    return *this;
}

example

class Fraction
{
    int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
    int n, d;
public:
    Fraction(int n, int d = 1) : n(n/gcd(n, d)), d(d/gcd(n, d)) { }
    int num() const { return n; }
    int den() const { return d; }
    Fraction& operator*=(const Fraction& rhs)
    {
        int new_n = n * rhs.n/gcd(n * rhs.n, d * rhs.d);
        d = d * rhs.d/gcd(n * rhs.n, d * rhs.d);
        n = new_n;
        return *this;
    }
};
std::ostream& operator<<(std::ostream& out, const Fraction& f)
{
   return out << f.num() << '/' << f.den() ;
}
bool operator==(const Fraction& lhs, const Fraction& rhs)
{
    return lhs.num() == rhs.num() && lhs.den() == rhs.den();
}
bool operator!=(const Fraction& lhs, const Fraction& rhs)
{
    return !(lhs == rhs);
}
Fraction operator*(Fraction lhs, const Fraction& rhs)
{
    return lhs *= rhs;
}
 
int main()
{
   Fraction f1(3, 8), f2(1, 2), f3(10, 2);
   std::cout << f1 << " * " << f2 << " = " << f1 * f2 << '\n'
             << f2 << " * " << f3 << " = " << f2 * f3 << '\n'
             <<  2 << " * " << f1 << " = " <<  2 * f1 << '\n';
}

Address of an overloaded function

int f(int) { return 1; }
int f(double) { return 2; }
 
void g( int(&f1)(int), int(*f2)(double) ) {}
 
template< int(*F)(int) >
struct Templ {};
 
struct Foo {
    int mf(int) { return 3; }
    int mf(double) { return 4; }
};
 
struct Emp {
    void operator<<(int (*)(double)) {}
};
 
int main()
{
    // 1. initialization
    int (*pf)(double) = f; // selects int f(double)
    int (&rf)(int) = f; // selects int f(int)
    int (Foo::*mpf)(int) = &Foo::mf; // selects int mf(int)
 
    // 2. assignment
    pf = nullptr;
    pf = &f; // selects int f(double)
 
    // 3. function argument
    g(f, f); // selects int f(int) for the 1st argument
             // and int f(double) for the second
 
    // 4. user-defined operator
    Emp{} << f; //selects int f(double)
 
    // 5. return value
    auto foo = []() -> int (*)(int) {
        return f; // selects int f(int)
    };
 
    // 6. cast
    auto p = static_cast<int(*)(int)>(f); // selects int f(int)
 
    // 7. template argument
    Templ<f> t;  // selects int f(int)
}