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
assignment | increment decrement | arithmetic | logical | comparison | member 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_cast
, const_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)
}