Results 1 to 3 of 3
  1. #1
    Yemiez's Avatar
    Join Date
    Jun 2012
    Gender
    male
    Location
    Sweden
    Posts
    2,566
    Reputation
    731
    Thanks
    16,280
    My Mood
    Devilish

    Unfinished C++11 'guide'-ish

    How to write good/effective C++11?
    NOTE: This contains C++11/14/17, you are expected to have some knowledge of C++ syntax
    & Computer architecture (Bytes, pointers, references).

    C++11 provides optimization abilities if you know how to
    utilize them.
    Most of C++11's features can be seen put to use in the STL library (Standard template library)
    But, note that the STL library has very hard to read syntax for anyone not familiar with templates & universal references.



     


    Constness
    To be able to fully understand the type deduction system/rules
    you need to understand the rules of const types & references/pointers, if the X's type is
    const (and the deducted type is not a reference to X) the constness will be removed.
    However when creating a reference to X the constness cannot be removed because
    it would violate the rules of constness.
    When it comes to pointers, most people would assume:
    Code:
    const int* ptr;
    Would mean that the value of 'ptr' is const, but this is wrong, the value of ptr
    can be changed at any time, but ptr points to a const int meaning it is read only:
    Code:
    *ptr = 5
    Would not compile (Error: Expression must be a modifiable Lvalue)
    (If you would like ptr to be a const pointer you must do: const int* const ptr)


    Basic Type Deduction

    In C++98 (C++ I most belive you were familiar with) the
    type deduction system was limited to templates, however
    in C++11 we have auto, decltype and alot more ways for you to write
    less code. This is possible because the compiler is able to 'detect'
    what X return type is and determine the return type at compile time, meaning
    creating variables using auto does not change code generation or similiar, it just
    makes it easier to read/write the actual code.
    To write poet like code you should always use the auto keyword when possible.

    Take a look at this code:
    Code:
    int x = 5;
    int& xx = x;
    const int& xxx = xx;
    const int xxxx = x;
    
    auto y = x;
    // The type of'y' is equal to int
    // Type deduction looks at the declaration of 'x' and determines it is a int.
    
    auto yy = xx; 
    // The type of 'yy' is equal to int.
    // Notice how using auto on a type of T& only 
    // the type T. This is very usefull because alot of the 
    // you want yy to get copied instead of using its 
    // However you can still easily make it a reference by doing auto&.
    
    auto yyy = xxx; 
    // Type of 'xxx' is equal to 
    // The deduction of const T& only 
    // the type T. (Its constness is 
    // The method to make it a reference is still the same.
    
    auto yyyy = xxxx; 
    // Type of 'yyyy' is equal to int. (Notice how the consness is removed)
    
    auto& xy = xxxx; 
    // Type of 'xy' is equal to const 
    // Notice how the consness is kept, 
    // the rules of const is taking over, you cannot create 
    // non-const reference to a const type (Unless using const_cast which is a whole other discussion).
    Notice how the constness is stripped from all auto's that are not references
    to a const lvalue.

    Type deduction on const & pointers:
    Code:
    int x = 5;
    int* xx = &x;
    const int* xxx = xx;
    
    auto v1 = x; 
    // v1 = int (Auto deduction is int.)
    
    const auto& v2 = xx; 
    // v2 = int*const& (A reference to a pointer that points to a const 
    // auto deduction = int*
    
    auto v3 = xxx; 
    // v3 = const int* (A pointer to a const 
    // Notice that the pointer points to a const int, the 
    // is not const, its what it points to. (Meaning you can assign the pointer to a new ptr)
    
    
    const auto* const v4 = v3; 
    // v4 = const int* const (A const pointer to a const 
    // auto deduction = const 
    // now the pointer is const, you can no longer change the address it points to.
    
    // When using auto for parameters the constness of the parameters
    // is ignored if the parameters is a pointer, however if it is a reference 
    // the constness over rules.
    // e.g:
    
    (const* int param1, const int* const param2, const int& param3)
    {
            auto v1 = param1; // v1 = const int*
        auto v2 = param2; // v2 = const int* (param2's constness ignored)
        auto v3 = param3; // v3 = int (Constness & referenceness is ignored)
        auto& v4 = param3; // v4 = const int&
    }



    That is the basics of type deduction, there is alot more to cover
    once you learn about Lvalues & Rvalues.

    Lets write some code from what we've learned!
    Code:
    // Create a stl vector of size 15
    std::vector<int> vec{ 15 };
    
    // An example of minimizing loop code greatly
    // is this:
    for (auto it = vec.begin(); it != vec.end(); ++it)
        *it = rand();
    
    // Instead of:
    for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it);
    Obviously this doesn't change code generation at all, but it improves readability.


    Decltype(auto) type deduction
    You might think decltype(auto) & auto are the same thing, but they are not.
    The reason as to why I started with talking about C++98's template deduction is because
    auto uses C++'s template deduction rules for deducing the desired type.
    The deduction rules for decltype(auto) is significantly different from auto's deduction rules.
    Here are some of the differences:
    Code:
    decltype(auto) x = y; 
    // y = int. x would be = int&
    // Whilst using auto x would = int.
    
    // This will cause some problems if you dont know the rules,
    // this could messup a whole program if you do this in a library you create or similiar.
    Here is an example of a function where it should return a non-reference:
    Code:
    auto lookupValue(const int& lookingfor)
    {
          auto vector = getVector();
          // Do some computing to find the value you are looking for...    
          return vector[idx]; // Returns the value that it compared to
    }
    • lookupValue returns a integer copy, as expected.
    • decltype(auto) would return a reference to the element in the vector, this is dangerous!


    An example where it should be used:
    Code:
    decltype(auto) get(const int& idx)
    {
        auto vector = getVector();
        return vector[idx];
    }
    It may be expected that a get function returns a reference to
    the element in question (e.g when creating a vector class you have at/operator[] method that return T&)


    We will talk more about type deducting in later chapters (As they are vital parts for understanding Rvalues/Lvalues & Move semantics)



     

    Rvalues & Lvalues, Whats the difference?
    Rvalues & its use is a bit hard to grasp at first, which is why I will go over
    them slowly and what they're used for, how to use them yourself, and hopefully
    by the end you will have an understanding of what the difference between
    RValues & Lvalues is, what each type is usefull for, and how to utilize them.
    So, lets start shall we?

    Lvalues are objects that are stored (They're life time extends past the current statement),
    they have an address (You can use the Address-of operator on them).
    Rvalues are values/objects that are not stored (They're life time do NOT extend past the current statement),
    you cannot use the address-of operator on them.
    If that was not clear, here is an example:
    Code:
    int x = 5; // Lvalue, stored untill the end of the current scope
    int y = 7; // Same as 'x'
    &x; // Totally valid line.
    &y; // Same
    
    x + y; // Rvalue, the result of x + y gets destroyed at the semicolon.
    x + y = 10; // Invalid line, rvalue on the left hand side of an assignment.
    int z = x + y; // Valid, rvalue on the right hand sife of an assignment.
    45; // Rvalue, gets destroyed at semicolon.
    You should now have a basic understanding of what Rvalues/Lvalues are, and the
    difference between them.


    Move Semantics & Perfect Forwarding
    Move Semantics & Perfect forwarding are almost always
    used together, these features must be implemented by yourself
    as the programmer. (But the STL provides functions for making it easier & possible)
    Lets start with Move semantics, as Perfect forwarding comes in the picture later.
    Move semantics should be used for every type you create (class), especially ones
    that are copy heavy (E.g takes up alot of memory, multiple inner vectors & or pointers etc).
    What move semantics does is take advantage of Rvalues, you remember how rvalues were
    destroyed once they reach the end of the statement, well. Since its gonna be destroyed anyway,
    why not gut it first? (swap its components with yours), essentially moving the memory from the rvalue
    into the current instance of the class.

    First lets go over some of the functions in the STL related to move semantics.
    std::move(lvalue);
    std::forward<T>(l/r value);

    The simplest way to probably explain what they actually do, is by telling you what they dont do.
    std::move does not actually move anything.
    std::forward does not actually forward anything.
    You can see them as a cast.
    std::move will always cast its argument to an rvalue.
    std::forward will conditionally cast its value to an rvalue.
    What I mean by conditionally cast is that if the argument is already of type
    rvalue it will cast it to an rvalue, if its an lvalue it will cast it to an lvalue. (You will realize why this is usefull later)

    Now suppose Foo is class that holds some resource, say _resource. This resource is big & very costly
    to copy, lets say it is a std::vector of type T and contains N elements.
    Code:
    Foo v1;
    Foo v2(v1); // Copy constructor is called (Foo(const Foo& other))
    Foo v3(std::move(v1)); // Move constructor is called (Foo(Foo&& other))
    // When v1 reaches the semicolon it will be destructed, but before its destructed
    // the contents of v3 is swapped with v1, meaning v3 holds everything v1 held.
    Now this doesn't mean you should always use move semantics, calling std::move on an lvalue
    basically destructs everything the lvalue contained, because thats how rvalues work, they get destructed at the end
    of the current statement for memory management (Created & Destroyed on the stack).

    Now that I've showed you how std::move is used, I should probably show you how you
    create your own move constructor, so it actually works.
    Code:
    // in class Foo
    Foo(Foo&& other)
    {
         // Now we just neatly swap the items in other with our own.
         std::swap(_resources, other._resources); 
    }
    That was simple right?

    The next part may confuse you a little at first, but its extremely usefull as you do dont have to
    re-create all your functions twice for an lvalue version & rvalue version. (std::forward is our saviour!):

    Suppose we have a class that holds a std::vector of type T with the name of _vec.
    Now in our class we have a function to add items to this vector, T may be a large user defined type, or similiar
    making it cost performance to copy a std::vector of T, hence why we want to use perfect forwarding when possible.
    Code:
    template<typename _T>
    void Add( _T&& element ) // when calling this function you should NOT do obj->Add<type> because 
    // perfect forwarding relies on type deduction using universal references (typename of name T with && becomes a
    // universal reference)
    {
       // Now universal references can be lvalues or rvalues (see where I'm getting?)
       _vec.push_back( std::forward<_T>(element) );
    }


    I'll add more stuff to this when I feel like it, if you want anything specific changed/added to the thread you can add my skype and suggest stuff to be changed/added. This was supposed to in the C++ section but since I didn't actually finish it I'll wait untill its finished untill I post it there.





    Resources: C++ Primer (5th), How to write Effective C++11 & Alot more resources by time (MSDN/cplusplus.com).
    Last edited by Hunter; 02-02-2016 at 05:10 AM.

  2. The Following 7 Users Say Thank You to Yemiez For This Useful Post:

    Hunter (01-02-2016),ImStyL (04-28-2016),k3jl7h (01-02-2016),Mixtape (01-23-2016),pean153 (01-04-2016),rwby (01-03-2016),unitedn (01-02-2016)

  3. #2
    bigcuntlol's Avatar
    Join Date
    Jul 2015
    Gender
    male
    Posts
    55
    Reputation
    10
    Thanks
    8
    Loooks... Good. Never heard of C++11 but ty

  4. #3
    Hunter's Avatar
    Join Date
    Dec 2013
    Gender
    male
    Location
    Depths Of My Mind.
    Posts
    17,468
    Reputation
    3771
    Thanks
    6,159
    My Mood
    Cheerful
    Bumping this since it's very useful.

Similar Threads

  1. Guide On Using Olly Debugger
    By Dave84311 in forum Game Hacking Tutorials
    Replies: 1
    Last Post: 12-14-2013, 11:12 PM