View on GitHub

trainingCpp

training C++11

https://mathieukapfer.github.io/trainingCpp

C++11

  1. C++11
    1. Loop
      1. Initialization
      2. Type inference and range-based for loop
    2. Class
      1. Canonical class declaration
      2. Usages
      3. Move
        1. implementation when class contains pointer
        2. implementation when class contains stl container
        3. implicit call on RVALUE
    3. Function
      1. Functor and Lambda expression
      2. Lambda expression closure
      3. Bind

Loop

Initialization

Stl container with initialisation list (as table)

C++98 style C++11 style
  std::vector<int> v;

  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
    // stl container with initialisation list (as table)
  std::vector<int> v = {1, 2, 3};

Type inference and range-based for loop

Nice and consise way to write loop !

C++98 style C++11 style
  for (std::vector<int>::iterator it=v.begin(); 
       it != v.end(); it++) {
    std::cout << *it << std::endl;
  }
  for (auto i: v) {
    std::cout << i << std::endl;
  }

This is not only cosmetic consideration, but also simplier ASM !

Class

Canonical class declaration

C++98 style C++11 style
class MyClass
{
 public:
  //! Default constructor
  MyClass();

  //! Copy constructor
  MyClass(const MyClass &other)

  //! Assignment operator
  MyClass& operator=(const MyClass &other)

  //! Destructor
  virtual ~MyClass();

};

class MyClass
{
 public:
  //! Default constructor
  MyClass() = default;

  //! Copy constructor
  MyClass(const MyClass &other) = default;

  //! Move constructor
  MyClass(MyClass &&other) noexcept = default;

  //! Destructor
  virtual ~MyClass() noexcept = default;

  //! Copy assignment operator
  MyClass& operator=(const MyClass &other) = default;

  //! Move assignment operator
  MyClass& operator=(MyClass &&other) noexcept = default;

};

C++11 new features:

Usages

Let’s inspect when each canonical method is called. (note that the implementations are not effective here, but just logged)

class MyClass
{
 public:
  //! Default constructor
  MyClass():m_i(0)
    { LOG_ENTER("default"); }

  //! Another constructor
  MyClass(int i): m_i(i)
    { LOG_ENTER("1 parameters"); }

  //! Copy constructor
  MyClass(const MyClass &other)
    { LOG_ENTER("copy");}

  //! Move constructor (C++11)
  MyClass(MyClass &&other) noexcept
    { LOG_ENTER("move"); }

  //! Destructor
  virtual ~MyClass() noexcept
    { LOG_ENTER(""); }

  //! Copy assignment operator
  MyClass& operator=(const MyClass &other)
    { LOG_ENTER("copy"); return *this;}

  //! Move assignment operator (C++11)
  MyClass& operator=(MyClass &&other) noexcept
    { LOG_ENTER("move"); return *this;}

  private:
  int m_i;

};
int main(int argc, char *argv[])
{
  // test constr
  MyClass c;               // default constr
  MyClass d(c);            // copy constr
  MyClass e(std::move(c)); // move constr
  MyClass f1(2);           // user defined constr with 1 parameter
  MyClass f2{1};           // same as above

  // test assignment operator
  d = e;                   // copy assignment operator
  e = std::move(d);        // move assignment operator

  // ! warning the code below is the declaration
  //   of a function. It do nothing !
  MyClass x();
}

With the following definition:

#define LOG_ENTER(str) cout << __FUNCTION__ << " " str " " << endl;

the execution produce:

MyClass default
MyClass copy
MyClass move
MyClass 1 parameters
MyClass 1 parameters
operator= copy
operator= move
~MyClass
~MyClass
~MyClass
~MyClass

Move

C++11 provide the move constructor and move assignment operator

implementation when class contains pointer

The idea behind the move constructor is to transfer the ownership of pointer (or stl container) of the class instead of copy them. Hence, we save a copy that may cost CPU.

Hereafter is a good implementation when the class carry a pointer

class BigClass
{

 private:
  static const int TABLE_SIZE=100;
  int *table;

 public:
  //! Default constructor
  BigClass():table(new int[TABLE_SIZE]) {
  }

  //! Move constructor
  BigClass(BigClass &&other) noexcept {
    std::cout << "Move constructor called" << std::endl;
    // copy the pointer ;
    table = other.table;
    // reset copied pointer
    other.table = nullptr;
    // => the ownerchip has changed
  };

}

implementation when class contains stl container

Just use the slt std::move(x) methode on each member.

implicit call on RVALUE

Definition:


#include <vector>
#include <iostream>

// a function to demonstrate the implicite call
//  of move operator
std::vector<int> getVector() {
  std::vector<int> tmp = {4, 5, 6};
  return tmp;
}

// helper to display container
#define display(v, msg)                         \
  std::cout << std::endl << msg ": " << #v "="; \
  for (int i:v) {std::cout << i << " ";};

int main(int argc, char *argv[])
{

  std::vector<int> v1 = {1, 2, 3};
  std::vector<int> v2, v3;

  display(v1,"after init");

  // explicity called
  v2 = std::move(v1);
  display(v1, "after move");
  display(v2, "after move");

  // implicilty called
  v3 = getVector();
  display(v3, "after fct ");

  return 0;
}

make move-getvector && ./move-getvector
g++     move-getvector.cpp   -o move-getvector

after init: v1=1 2 3
after move: v1=
after move: v2=1 2 3
after fct : v3=4 5 6

Function

Functor and Lambda expression

Definition:

Usage: The two pieces of code do the same thing

C++98 style C++11 style

#include <algorithm>  // for_each (C++98)
#include <vector>
#include <iostream>

using namespace std;


// functor
struct F {
  void operator() (int i) {
    cout << i << endl;
  };
} f;

// classic function
void fct(int i) {
  cout << ' ' << i << endl;
}

int main()
{
  vector<uint> v;

  v.push_back(1);
  v.push_back(2);
  v.push_back(3);

  // use functor
  for_each (v.begin(), v.end(), f);

  // use classic function
  for_each (v.begin(), v.end(), fct);

  return 0;
}

#include <algorithm>
#include <vector>
#include <iostream>

using namespace std;

int main()
{
  // C++11: initialisation list
  //        work with stl container
  vector<int> v = {1, 2, 3};

  // C++11: use lambda expression
  //        (i.e. anonymous function)
  for_each
    (v.begin(), v.end(),
       [] (int item) {cout << item << endl;});

}

Lambda expression closure

The syntax [] if a lambda function is called the ‘‘closure’’.

It define how to capture local variables : by reference, by value, all variables, only one.

syntaxe meaning
[] no capture
[x, &y] x by value, y by reference
[=] all by value
[&] all by reference
[&, x] all by reference except x
[=, &x] all by value except x
int main()
{
  // stl container
  vector<int> v = {1, 2, 3};

  // lambda funct
  // with capture (of local variable)
  int sum = 0;

  for_each
    (v.begin(), v.end(),
      [&sum] (int item) {sum += item;});

  cout << "sum:" << sum << endl;
}

Bind

The std::bind function is part of <functional>.

It allow to create a new function by wrapping an existing one i.e changing the signature, in order to reduce the number of parameters.

The bind object may be a function, functor, or method.

#include <functional>
#include <iostream>

// function
int add(int a, int b) {
  return a + b;
}

int main()
{

  // call functor
  std::cout << f(1,2) << std::endl;

  // bind function - keep one parameter (the second one), replace the first one by 1000
  std::function<int (int)> add_1000 = std::bind(add, std::placeholders::_1, 1000);

  // call binding function
  std::cout << add_1000(1) << std::endl;

  return 0;
}