cpp温故知新(c++20)

gerneral all

storage duration

  • automatic storage duration The storage for the object is allocated at the beginning of the enclosing code block and deallocated at the end. All local objects have this storage duration, except those declared static, extern or thread_local.

  • static storage duration The storage for the object is allocated when the program begins and deallocated when the program ends. Only one instance of the object exists. All objects declared at namespace scope (including global namespace) have this storage duration, plus those declared with static or extern.

  • thread_local storage duration The storage for the object is allocated when the thread begins and deallocated when the thread ends. Each thread has its own instance of the object. Only objects declared thread_local have this storage duration. thread_local can appear together with static or extern to adjust linkage.

  • dynamic storage duration The storage for the object is allocated and deallocated per request by using dynamic memory allocation functions.

linkage

  • no linkage The name can be referred to only from the scope it is in. variables in block scope(except extern)

  • static linkage The name can be referred to from all scopes in the current translation unit.

variable in namespace scope 1. variabales in namespace scope declared static 2. const-qulified(or constexpr) variable not declared extern 2. data member in anonymous unions 3. variables in anonymous namespace

  • external linkage other variable in namespace scope

perfect Forwarding

it used to reserve the r or l value property of a parameter in generic context.

emplace_back vs push_back

  1. emplace_back cannot use to initilize aggregated things.(up to c++17, it is specialized to none list in-place construction)

container

  1. vector vs deque for small size use vector, otherwise use deque instead.

new features

  1. structured binding auto [res1, res2] = ...

  2. init statement of if or switch

  3. constexpr if

  4. c++11 unknow before
  • function reload
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class Test {
    private:
    std::string name{"wens"};

    public:
    std::string getname() && { printf("in && function\n"); return name;}
    const std::string& getname() const& { printf("in const& function\n"); return name;}
    };


    int main() {

    Test t;
    auto v = t.getname();
    printf("%s\n", typeid(v).name());

    auto a = Test{}.getname();
    printf("%s\n", typeid(a).name());



    getchar();
    return 0;

    }

  1. constexpr
    may be available at compile-time but not guarantee
    consteval
    must evaluate at commpile time

    1
    2
    3
    if consteval {

    }

    you cannot evaluate runtime functions at compile-time

  2. value categories

expression -> glvalue(generalized lvalue) and rvalue glvalue -> lvalue and xvalue(expiring value) rvalue -> xvalue and prvalue(pure rvalue)

simple: lvalue: everything has name and string literal (identiy of an object) prvalue: temporaries and other literal (perform initialization/compute an object) xvalue: value from std::move() (denote an object whose resource can be reused) glvalue produce locations, prvalue preform initialize

c++17: prvalue perform initialization -> no temporary object yet glvalue produce locations materialization to get temporary object -> prvalue to xvalue conversion

lambda funcion vs std::function

  1. std::function cannot inline by compiler
  2. when lambda with capture assigned to std::function, std::function will do heap allocation(some implementation may not heap-alloacte when less than specific threshhold)

sizeof & alignof

  1. alignof(T) = max(alignof(member1), alignof(member2)...)
  2. sizeof(T) % alignof(T) == 0

determine compile type

  1. _MSC_VER
  2. __clang__
  3. __GNUC__

copy ellison for initialization from temporaries(prvalues) is required in c++17

callable copy/move constructor no longer required

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Test {
public:
Test() = default;
//no copy/move constructor
Test(const Test& ) = delete;
Test(Test&& ) = delete;

};


void foo(Test t) {

}
foo( Test() ) //is ok in c++17

auto decays

  1. raw arrays convert to pointers
  2. functions convert to function pointers
  3. top-level references are removed
  4. top-level const/volatile are removed

initialization

  • to pass mutiple parameters to ordinary constructors different types ok

  • as an std::initializer_list<> must be homogeneous(same types or can conversion to the same type) only for {}

  • () initialization for ordinary constructor only {} initialization for all constructors
    1. std::initialization_list<> constructors has higher priority
    2. but the default constructor has the highest priority
  • to aggregate object wiht initializer list{}, member is value initiaized without any initializer list and no default member initialization, then uninitialized

enum initializaiont with integer

  1. only with direct list initialization
  2. if the enum has fixed underlying type
  3. since c++17
1
2
3
enum class Salution{mr, ms};

Salution s{17}

uniform initialization is not quite work with atomic and assert(maybe fixed int c++20)

  1. atomic
1
2
3
4
5
6
7
8
9
10
11
12
//with P0883 maybe fixed in c++20
std::atomic<int> x1; //does zero initialize
std::atomic<int> x2{}; //does not zero initialize

struct counter {
int external_counter = 0;
int count = 1;
};

std::atomic<counter> c1; //does not initialize with 0 and 1
std::atomic<counter> c2; //does also not initialize with 0 and 1

  1. assert
    1
    2
    3
    assert(c == std::complex(0, 0)); //ok
    assert(c == std::complex{0, 0}); //does not compile
    assert((c == std::complex{0, 0})); //ok
  1. registers rax(64) -> eax(32) -> ax(16) -> ah(8) and al(8) (left bits zeroed)

rax: return value rdi: 1st param rsi: 2nd param

  1. Underscore (_) is special, as C++ reserves the following to be used by the compiler and the standard library
  • any identifier that starts with an _ followed by an upper-case letter, and
  • any identifier that contains two consecutive underscores (i.e. __) anywhere in its name
  1. platform judge
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #if defined(_WIN32)
    /* we're on windows */
    #elif defined(__GNUC__) && !defined(__clang__)
    /* we're on gcc ! */
    #elif defined(__clang__)
    /* we're on clang */
    #else
    /* ¯\_(ツ)_/¯ */
    #endif

practical performance practices

  1. const & initialize
  • always const
  • always initialize
  • use IIFE can help you initialize
  • don't recalculate values that can be calculated once
  1. list vs vector vs array vs containers
  • always prefer std::array
  • then std::vector
  1. others
  • avoid object copying
  • avoid automatic conversion: using conversion explicit
  • don't pass smart pointers
  • avoid std::endl
  • prefer return std::unique_ptr to std::shared_ptr

shared_ptr thread safe

  1. atomic smart pointers

spinlock

  • in user mode or general application mode, you should avoid spinlock.
  • in kernel mode(low-level stuff), you only hold the lock about several instructions and latency is important, a spinlock maybe better solution than a lock.

c++17&c++20 new features

  • c++17 fold expressions, typename in template parameters, auto for non-type template parameter, class template argument deduction inline variable(constexpr imply inline now), nested namespace

  • c++20 template lambdas, string literals as non-type template parameters, constrains(template requires), concepts

GotW by herb

overload、override& hide

static type and dynamic type

virtual destructor

  1. overload resolution & default parameter are on static type.
  2. override is on dynamic type.
  3. virtual dtor
    1
    2
    3
    Base *b = new Derived();
    // use b
    delete b; // Here's the problem!
    > [In delete b], if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.

If you want to prevent the deletion of an instance through a base class pointer, you can make the base class destructor protected and nonvirtual; by doing so, the compiler won't let you call delete on a base class pointer.

Because once execution reaches the body of a base class destructor, any derived object parts have already been destroyed and no longer exist. If the Base destructor body were to call a virtual function, the virtual dispatch would reach no further down the inheritance hierarchy than Base itself. In a destructor (or constructor) body, further-derived classes just don't exist any more (or yet).

inline function

the declaration and definition are the same. > so the member function which definition with declaration maybe treated as inline by compiler

function modifiers

  1. const
    1
    2
    3
    4
    5
    6
    7
    8
    // value parameter: top-level const is not part of function signature
    int f( int );
    int f( const int ); // redeclares f(int): this is the same function
    // here you can declare f(int), then when define it, using f(const int) to refer const of the parameter

    // non-value parameter: top-level const is part of function signature
    int g( int& );
    int g( const int& ); // overloads g(int&): these are two functions

header include

  1. parameter and return types only need to be forward-declared
-------------本文结束感谢您的阅读-------------