Immutable programming in C++

                   Zoltán Porkoláb
                     gsd@elte.hu

              Eötvös Loránd University
                 Budapest, Hungary





           Agenda



 - Pure functions
 - Const in C++
 - Const correctness in C++
 - Constexpr in C++11 and 14
 - Lambdas and const




Constants in C++
================





Design goals of C++

 - Type safety

        C++ is a statically strongly typed programming language
        The compiler decides the type of all (sub)expression in compile time

        In run time: pointers, conversions, Object-oriented constructs


 - Resource safety

        Not just memory! All resources (files, sockets, locks, etc.)
        No garbage collection by def. (but can implement)
        Use the RAII (Resource Acquisition Is Initialization) idiom

        Most beginners make resource errors

 - Performance

        Direct access to HW resources. No virtual machine
        High performance trading, phone exchange systems
        Low energy cosumption (Mars rover)

- Predictability

        Large systems (2-10 million eLoC)
        Orthogonal features should work well together

- Learnability, readability

        C++11: from expert-friendly to novice-friendly



C++ is rich to express semantical concepts by syntax



#include <stdio.h>

int main()   // wrong C program
{
    FILE *fp = fopen( "input.txt", "r");

    // ...

    fprintf( fp, "%s\n", "Hello input!");

    fclose(fp);
    return 0;
}


$ gcc -std=c99 -pedantic -Wall -W wrong.c
$ ./a.out
Segmentation violation



#include <fstream>

int main()
{
    std::ifstream f;

    // ...

    f << "Hello input!" << std::endl;

    return 0;
}



$ g++ -std=c++0x wrong.cpp

wrong.cpp: In function ‘int main()’:
wrong.cpp:10:8: error: no match foroperator<<’ in ‘f << "Hello input!"’

wrong.cpp:10:8: note: candidates are:
/usr/include/c++/4.6/ostream:581:5: note: template<class _CharT, class _Traits, class _Tp> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&)
/usr/include/c++/4.6/ostream:528:5: note: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const unsigned char*)
/usr/include/c++/4.6/ostream:523:5: note: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const signed char*)
/usr/include/c++/4.6/ostream:510:5: note: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const char*)
/usr/include/c++/4.6/bits/ostream.tcc:323:5: note: template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const char*)
/usr/include/c++/4.6/ostream:493:5: note: template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const _CharT*)
/usr/include/c++/4.6/ostream:473:5: note: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, unsigned char)
/usr/include/c++/4.6/ostream:468:5: note: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, signed char)
/usr/include/c++/4.6/ostream:462:5: note: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, char)
/usr/include/c++/4.6/ostream:456:5: note: template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, char)
/usr/include/c++/4.6/ostream:451:5: note: template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, _CharT)
/usr/include/c++/4.6/bits/basic_string.h:2693:5: note: template<class _CharT, class _Traits, class _Alloc> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::basic_string<_CharT, _Traits, _Alloc>&)




Constant correctness




//
// fahr2cels: using const
//

#include <iostream>
#include <iomanip>

int lower = -100;
int upper =  400;
int step  =   40;

int main()    // fahr2cels
{
    for( int fahr = lower; upper >= fahr; fahr += step )
    {
        std::cout << "fahr = " << std::setw(4) << fahr
                  << ", cels = " << std::fixed << std::setw(7)
                  << std::setprecision(2) << 5./9. * (fahr-32)
                  << std::endl;
    }
    return 0;
}



$ g++ -std=c++0x fahr.cpp
$ ./a.out
fahr = -100, cels =  -73.33
fahr =  -60, cels =  -51.11
fahr =  -20, cels =  -28.89
fahr =   20, cels =   -6.67
fahr =   60, cels =   15.56
fahr =  100, cels =   37.78
fahr =  140, cels =   60.00
fahr =  180, cels =   82.22
fahr =  220, cels =  104.44
fahr =  260, cels =  126.67
fahr =  300, cels =  148.89
fahr =  340, cels =  171.11
fahr =  380, cels =  193.33




But only a small modification in the code ....


#include <iostream>
#include <iomanip>

int lower = -100;
int upper =  400;
int step  =   40;

int main()    // fahr2cels: wrong!
{
    for( int fahr = lower; upper = fahr; fahr += step )
    {
         std::cout << "fahr = " << std::setw(4) << fahr
                   << ", cels = " << std::fixed << std::setw(7)
                   << std::setprecision(2) << 5./9. * (fahr-32)
                   << std::endl;
    }
    return 0;
}


$ g++ -std=c++0x fahr.cpp
$ ./a.out



...may result infinite loop




But, if we use constants



#include <iostream>
#include <iomanip>

const int lower = -100;
const int upper =  400;
const int step  =   40;

int main()
{
    for( int fahr = lower; upper = fahr; fahr += step )
    {
         std::cout << "fahr = " << std::setw(4) << fahr
                   << ", cels = " << std::fixed << std::setw(7)
                   << std::setprecision(2) << 5./9. * (fahr-32)
                   << std::endl;
    }
    return 0;
}



$ g++ -std=c++0x fahr.cpp
fahr.cpp: In function ‘int main()’:
fahr.cpp:10:34: error: assignment of read-only variable ‘upper’




Use the compilation flags for warnings!



$ g++ -std=c++0x -pedantic -Wall -W fahr.cpp
fahr.cpp: In function ‘int main()’:
fahr.cpp:10:34: error: assignment of read-only variable ‘upper’
fahr.cpp:10:34: warning: suggest parentheses around assignment used as truth value [-Wparentheses]






What kind of constants are in C++



Preprocessor macros

 - Replaced by literals



#include <iostream>
#include <iomanip>

#define LOWER  -100
#define UPPER   400
#define STEP     40

int main()
{
    for( int fahr = LOWER; UPPER = fahr; fahr += STEP )
    {
        std::cout << "fahr = " << std::setw(4) << fahr
                  << ", cels = " << std::fixed << std::setw(7)
                  << std::setprecision(2) << 5./9. * (fahr-32)
                  << std::endl;
    }
    return 0;
}



$ g++ -std=c++0x  fahr.cpp
fahr.cpp: In function ‘int main()’:
fahr.cpp:10:34: error: lvalue required as left operand of assignment




Disadvantages

 - No scope
 - No lifetime
 - No type  (the replacing literal has type)






String literals


 - Values are known at compile-time:



const char *hello1 = "Hello world";
      char *hello2 = "Other hello";



hello1[1] = 'a';    // syntax error
hello2[1] = 'a';    // could cause runtime error


char *s = const_cast<char *>(hello1);   // dangerous

s[3] = 'x';         // could be runtime error !



This kind of data stored outside of the program writeable area.
Therefore write attempts are undefined.

There is difference between a string literal and an initialized character
array.


#include <iostream>

int main()
{
    // declaration of three arrays in the user data area
    // read and write permissions for the elements:
    char t1[] = {'H','e','l','l','o','\0'};
    char t2[] = "Hello";
    char t3[] = "Hello";

    // declaration of two pointers in the user data area
    // read and write permissions for the pointers
    // ...and...
    // allocation of the "Hello" literal (possibly) read-only
    char  *s1 = "Hello";    // s1 points to 'H'
    char  *s2 = "Hello";    // ... and s2 likely points to the same place  

    void  *v1 = t1, *v2 = t2, *v3 = t3, *v4 = s1, *v5 = s2;
    std::cout <<v1<<'\t'<<v2<<'\t'<<v3<<'\t'<<v4<<'\t'<<v5<<std::endl;
    // the result (v1, v2 v3 are different, v4 and v5 could be the same):
    0xbffff460   0xbffff450    0xbffff440   0x8048844   0x8048844

    // assignment to array elements:
    *t1 = 'x'; *t2 = 'q'; *ct = 'y';

    // modifying string literal: could be segmentation error:
    *s1 = 'w'; *s2 = 'z';

    return 0;
}










Named constants





 Named constants have special linkage rules


 - const has no linkage - it is "local" to source file
 - extern const has linkage
 -    we have to distinguish declaration and definition



// file1.cpp                           // file2.cpp

// definition of non-extern const      // definition of non-extern const
const int ic = 10;                     const int ic = 20;

// definition of extern const          // declaration of extern const
extern const int ec = 30;              extern const int ec;







They know more what you expected



#include <iostream>


int f(int i) { return i; }

int main()   // switch.cpp
{
    const int c1 = 1;    // initialized compile time
    const int c2 = 2;    // initialized compile time
    const int c3 = f(3); // f() is not constexpr:  initialized run-time

    int t1[c1];
    int t2[c2];
    // int t3[c3]; // ISO C++ forbids variable-size array `t3'



    int i;  std::cin >> i;
    switch(i)
    {
    case c1: std::cout << "c1"; break;
    case c2: std::cout << "c2"; break;
    // case label does not reduce to an integer constant
    // case c3: std::cout << "c1"; break;
    }
    return 0;
}


 - Since C++11 we can use constexpr functions - see later






Does a const requires to allocate memory?


:


#include <iostream>


int f(int i) { return i; }

int main()   // memory.cpp
{
    const int c1 = 1;    // no memory needed
    const int c2 = 2;    // need memory if f is not constexpr
    const int c3 = f(3); // need memory
    const int *p = &c2;

    int t1[c1];
    int t2[c2];
    // pre-C++11 ISO C++ forbids variable-size array `t3'
    int t3[c2];

    return 0;
}




The compiler have to allocate memory


 - when the const is initialized at run-time
 - when there exists at least one pointer pointing to that const




Proving





#include <iostream>

int f(int i) { return i; }

int main()   // memory.cpp
{
    int mark1;
    const int c1 = 1;    // no memory needed
    const int c2 = 2;    // no memory needed
    const int c3 = f(3); // need memory if f not constexpr
    int mark2;
    // const int *ptr = &c2;  

    size_t m1 = reinterpret_cast<size_t>(&mark1);
    size_t m2 = reinterpret_cast<size_t>(&mark2);
    std::cout << m1 << std::endl;
    std::cout << m2 << std::endl;
    std::cout << "distance = " << ( m1>m2 ? m1-m2 : m2-m1 ) << std::endl;
    return 0;
}




$ g++ -std=c++0x  -pedantic -Wall -W memory.cpp
$ ./a.out
140734134410892
140734134410888
distance = 4




#include <iostream>

int f(int i) { return i; }

int main()   // memory.cpp
{
    int mark1;
    const int c1 = 1;    // no memory needed
    const int c2 = 2;    // need memory 
    const int c3 = f(3); // need memory if f not constexpr
    int mark2;
    const int *ptr = &c2;

    size_t m1 = reinterpret_cast<size_t>(&mark1);
    size_t m2 = reinterpret_cast<size_t>(&mark2);
    std::cout << m1 << std::endl;
    std::cout << m2 << std::endl;
    std::cout << "distance = " << ( m1>m2 ? m1-m2 : m2-m1 ) << std::endl;
    return 0;
}




$ g++ -std=c++0x  -pedantic -Wall -W memory.cpp
$ ./a.out
140734135699932
140734135699924
distance = 8






But be care with optimizations!



#include <iostream>


int main()
{
    const int ci = 10;
    int *ip = const_cast<int*>(&ci);

    ++*ip;

    cout << ci << " " << *ip << endl;    // could be '10 11'
    return 0;
}






Constant correctness
====================




Motivation



 - Suppose we try to implement strlen() ...
 - ... and we make mistakes




#include <iostream>

int my_strlen(char *s)
{
    char *p = s;

    while ( ! (*p = 0) ) ++p; // here the programmer made fatal error
    return p - s;
}

// This program likely cause run-time error
int main()
{
    char t[] = "Hello";
    std::cout << my_strlen(t) << std::endl;
    return 0;
}


$ g++ -std=c++0x  -pedantic -Wall -W strlen.cpp
$ ./a.out
Segmentation violation
$






Try to do it in a constant correct way

 - Since strlen() don't want to modify the string declare it to const
 - const char *  is a "pointer to constant"
 - Not the pointer is the constant - but the pointed memory




#include <iostream>

// this is the correct declaration of the parameter
int my_strlen(const char *s)
{
    // ... which requires const char* here:
    const char *p = s;

    // In function `int my_strlen(const char*)':
    // assignment of read-only location * p
    while ( ! (*p = 0) ) ++p;
    return p - s;
}

int main()
{
    char t[] = "Hello";
    std::cout << my_strlen(t) << std::endl;
    return 0;
}



$ g++ -std=c++0x  -pedantic -Wall -W strlen.cpp
strlen.cpp: In function ‘int my_strlen(const char*)’:
strlen.cpp:6:21: error: assignment of read-only location ‘* p’
$



This reveals the mistakes we made, and we can fix it,
but this requires all affected variables to declare
as pointer to const.


This is the correct version:




#include <iostream>


// this is the correct declaration of parameter
int my_strlen(const char *s)
{
    // ... which requires const char* here:
    const char *p = s;

    while ( ! (*p == 0) ) ++p;
    return p - s;
}

int main()
{
    char t[] = "Hello";

    std::cout << my_strlen(t) << std::endl;  // t converted to const char *
    return 0;
}





A side note about defensive programming




#include <iostream>

int my_strlen(char *s)
{
    char *p = s;

    while ( ! (0 == *p) ) ++p; // write immutable on the left side
    return p - s;
}






const correctness and pointers
==============================





       int i = 4;  // not constant, can be modified: 
           i = 5;

const int ci = 6;   // const, must be initialized
          ci = 7;   // syntax error, cannot be modified


     int *ip;
          ip = &i;
         *ip = 5;   // ok






Now lets try a trick



          ip = &ci; // ??
         *ip = 7;   // can I do this?

          ip = &ci; // syntax error



const int *cip = &ci; // ok 
          *cip = 7;   // syntax error



          ip = cip;   // syntax error, C++ keeps constness
         cip = ip;    // ok, but now:
        *cip = 5;     // syntax error, wherever cip points is const 



int const *icp;       // same as const int *
           icp = &i;  // ok, can assign to, icp is NOT const, *icp IS


          *icp = 5;   // syntax error, wherever icp points is const







Can a pointer to be const?


:


int * const ipc = &i; // ipc is const, must be initialized         
           *ipc = 5;  // OK, where ipc points to is NOT a const


int * const ipc2 = &ci; // syntax error, ipc is NOT pointer to const


const int * const cccp = &ci;  // const pointer to a const






What about the user defined types?


:


class Date
{
public:
    Date( int year, int month = 1, int day = 1);
    // ...

    int getYear();
    int getMonth();
    int getDay();

    void set(int y, int m, int d);
    // ...
private:
    int year;
    int month;
    int day;
};



const Date my_birthday(1963,11,11);
      Date curr_date(2015,7,10);


my_birthday =  curr_date;  // syntax error: my_birthday is const





 ... but ...







int year = my_birthday.getYear(); // can we assure, getYear will not modify?
my_birthday.set(2015,7,10);       // can we assure, set will not modify?




class Date
{
public:
    Date( int year = 2000, int month = 1, int day = 1);
    // ...

    int getYear() const;
    int getMonth() const;
    int getDay() const;

    void set(int y, int m, int d);
    // ...
private:
   int year;
   int month;
   int day;
};





const Date my_birthday(1963,11,11);
      Date curr_date(2015,7,10);




int year = my_birthday.getYear(); // ok
int year = curr_date.getDay();    // ok

curr_date.set(2015,7,11);         // ok
my_birthday.set(2015,7,10);       // syntax error






const and mutable members



Const members are immutable under the object lifetime



class Msg
{
public:
    Msg(const char *t);
    int getId() const { return id; }
private:
    const int id;
    std::string txt;
};




Msg m1("first"), m2("second");
m1.getId() != m2.getId();



MSg::Msg(const char *t)
{
    txt = t;
    id  = getNewId();  // syntax error, id is const
}



MSg::Msg(const char *t) : id(getNextId()), txt(t)  // initialization list
{
}





Mutable members can be changed even from const member functions





struct Point
{
    void getXY(int& x, int& y) const;

    double xcoord;
    double ycoord;
    mutable int read_cnt;
};


const Point a;
++a.read_cnt;  // ok, Point::read_cnt is mutable





#include <mutex>


struct Point
{
public:
    void getXY(int& x, int& y) const;
    // ...
private:
    double  xcoord;
    double  ycoord;
    mutable std::mutex m;
};


// atomic read of point 
void getXY(int& x, int& y) const
{
    std::lock_guard< std::mutex > guard(m); // locking of m

    x = xcoord;
    y = ycoord;

} // unlocking m






Static const are like non-member const but in class namespace.





//  Static constants in class 


// file: a.h
class X
{
    static const int  c1 = 7;    // ok, but remember definition
    static       int  i2 = 8;    // error: not const
    const        int  c3 = 9;    // error: not static
    static const int  c4 = f(2); // error: initializer not const
    static const float f = 3.14; // error: not integral 
};


const int X::c1;  // do not repeat initializer here...






Overloading on immutability




template <typename T, ... >
class std::vector
{
public:

    T&       operator[](size_t i);
    const T& operator[](size_t i) const;

    // ...
};



int main()
{

          std::vector<int>   iv;
    const std::vector<int>  civ;

    // ...

    iv[i] = 42;       // non-const
    int i = iv[5];

    int j = civ[5]    // const 

    // ...    
}




The Standard Template Library is const-safe
===========================================




template <typename It, typename T>
It find( It begin, It end, const T& t)
{
    while (begin != end)
    {
        if ( *begin == t )
        {
            return begin;
        }
        ++begin;
    }
    return end;
}




const char t[] = { 1, 2, 3, 4, 5 };

const char *p = std::find( t, t+sizeof(t), 3)

if ( p )
{
    std::cout << *p; // ok to read
    // syntax error: *p = 6;
}


const std::vector<int>  v(t, t+sizeof(t));

std::vector<int>::const_iterator i = std::find( v.begin(), v.end(), 3);

if ( v.end() != i )
{
     std::cout << *i; // ok to read
    // syntax error: *i = 6;
}



//
//  C++11
//

std::vector<int>  v1(4,5);
auto i = std::find( v1.begin(), v1.end(), 3);   // i is vector::iterator

const std::vector<int>  v2(4,5);
auto j = std::find( v2.begin(), v2.end(), 3);   // j is vector::const_iterator


auto k = std::find( v1.cbegin(), v1.cend(), 3); // k is vector::const_iterator






//
// issue in C++98:
//

std::vector<int>  v(3,5);
std::vector<int>::const_iterator ci = std::find( v.begin(), v.end(), 3);
v.insert(ci, 2);  // syntax error in C++98


std::vector<int>  v(3,5);
auto ci = std::find( v.cbegin(), v.cend(), 3);
v.insert(ci, 2);  // works in C++11 (from g++ 4.9.2)




//
//  C++11: use global begin() and end()
//

std::vector<int>  v(4,5);
auto i = std::find( begin(v), end(v), 3);   // i is vector::iterator


// fails in C++11
std::vector<int>  v(4,5);
auto i = std::find( cbegin(v), cend(v), 3); // failed to add cbegin, cend


// fixed in C++14
std::vector<int>  v(4,5);
auto i = std::find( cbegin(v), cend(v), 3);





Constexpr
=========




Constexpr in C++11




Constexpr objects

  const objects having value that is known at translation time.
  translation time = compilation time + linking time

  they may have placed to ROM

 - immediately constructed or assigned
 - must contain only literal values, constexpr variables and functions
 - the constructor used must be constexpr constructor





Constexpr function

 can produce constexpr values when called with compile-time constants.


 - must not be virtual
 - return type must be literal type
 - parameters must be literal type


 Literal types:
   - all build-in types (except void)
   - user-defined types with constexpr constructor


 - C++11:
      Only contain a single expression (return statement)
      constexpr non-static memberfunctions are implicitly const


 - C++14:
      declaring variable that is not static or local_thread
      if / else / switch
      for / ranged-for / while / do-while
      objects with lifetime began in constexpr can mutate

      removes the rule that constexpr non-static memberfunctions are implicitly const
      additional restrictions for static and local_thread variables


 - cannot contain
      asm definition
      goto
      try block


Constexpr constructor

 - parameters must be literal type
 - must have no virtual base classes
 - either deleted or defaulted or contain only the following:
      ...as above...

 - the constructor must not have a function-try block
 - base class and every non-static member must be initialized
 - every implicit conversion involved must be a constant expression



Samples (from Scott Meyers: Effective Modern C++)





// ok in C++11:
constexpr int pow( int base, int exp) noexcept
{
    return  exp == 0 ? 1 : base * pow(base, exp-1) ;
}


// ok in C++14
constexpr int pow( int base, int exp) noexcept
{
    auto result = 1;

    for (int i = 0; i < exp; ++i) result *= base;

    return result;
}






// literal type
class Point
{
public:
    constexpr Point(double xVal = 0, double yVal = 0) noexcept
                : x(xVal), y(yVal)  {}
    constexpr double xValue() const noexcept { return x; }
    constexpr double yValue() const noexcept { return y; }

    // can be constexpr in C++14
    void setX(double newX) noexcept { x = newX; }
    void setY(double newY) noexcept { y = newY; }
private:
    double x, y;
};



constexpr Point p1(42.0, -33.33); // fine, "runs" constexpr
                                  // ctor during compilation
constexpr Point p2(25.0, 33.3);   // also fine



constexpr Point midpoint(const Point& p1, const Point& p2) noexcept
{
    return { (p1.xValue() + p2.xValue()) / 2,   // call constexpr
             (p1.yValue() + p2.yValue()) / 2 }; // member funcs
}
constexpr auto mid = midpoint(p1, p2); // init constexpr
                                       // object w/result of
                                       // constexpr function



// in C++14, where setX and setY are constexpr:
// return reflection of p with respect to the origin (C++14)
constexpr Point reflection(const Point& p) noexcept
{
    Point result;             // create non-const Point
    result.setX(-p.xValue()); // set its x and y values
    result.setY(-p.yValue());
    return result;            // return copy of it
}


constexpr auto midReflected = reflection(mid);




Template metaprograms only emulates floating point vales.







#include <iostream>

constexpr int cstrlen(const char *s)
{
    const char *p = s;
    while ( '\0' != *p )  ++p;
    return p-s;
}

int main()
{
    constexpr int len = cstrlen("Hello");
    std::cout << len << std::endl;
    return 0;
}



$ g++ -std=c++11 -pedantic -Wall -W conststrlen.cpp
conststrlen.cpp: In function ‘constexpr int strlen(const char*)’:
conststrlen.cpp:8:1: error: body of constexpr function ‘constexpr int strlen(const char*)’ not a return-statement




$ g++ -std=c++14 -pedantic -Wall -W conststrlen.cpp
$ ./a.out
5
$








Avoiding side effects




constexpr int def_size(int n)
{
    static int value = n;   // error
    return value;
}




int x;                              // not constant

struct A
{
    constexpr A(bool b) : m(b?42:x) { }
    int m;
};



constexpr int v = A(true).m;        // OK: constructor call initializes
                                    // m with the value 42

constexpr int w = A(false).m;       // error: initializer for m is
                                    // x, which is non-constant

constexpr int f1(int k)
{
    constexpr int x = k;            // error: x is not initialized by a
                                    // constant expression because lifetime of k
                                    // began outside the initializer of x
    return x;
}


constexpr int f2(int k)
{
    int x = k;                      // OK: not required to be a constant expression
                                    // because x is not constexpr
    return x;
}




constexpr int incr(int &n)
{
    return ++n;
}


constexpr int g(int k)
{
    constexpr int x = incr(k);      // error: incr(k) is not a core constant
                                    // expression because lifetime of k
                                    // began outside the expression incr(k)
    return x;
}


constexpr int h(int k)
{
    int x = incr(k);                // OK: incr(k) is not required to be a core
                                    // constant expression
    return x;
}


constexpr int y = h(1);             // OK: initializes y with the value 2
                                    // h(1) is a core constant expression because
                                    // the lifetime of k begins inside h(1)





The non-template metaprogram factorial function




constexpr auto factorial(unsigned n)
{
    unsigned long long res = 1;

    while ( n > 1) res *= n--;

    return res;
}

int main()
{
    static_assert(120 == factorial(5), "loop version incorrect");
    return 0;
}






Lambdas and constness
=====================




 - C++11: lambda functions
 - C++14: generalized lambdas






int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
    cout << endl;
    return 0;
}



$ g++ l.cpp
$ ./a.out
0 1 2 3 4 5 6 7 8 9
$


[]        lambda-introducer
(int n)   lambda parameter declaration
{ ... }   compound statement



Lambda expressons define classes and construct objects.
The program above is equivalent to:



struct LambdaFunctor
{
    void operator() (int n) const { cout << n << " "; }  // const member!
};

int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    for_each(v.begin(), v.end(), LambdaFunctor());
    cout << endl;
    return 0;
}



Definitions from Meyers' Effective Modern C++:

Lambda expression:  [](int n) { cout << n << " "; }
Closure: runtime object created from lambda. May hold captured variables.
Closure class: the type of the closure object.



Capture by value



int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    int x = 0;
    int y = 0;
    cin >> x >> y;

    v.erase( remove_if(v.begin(),
                       v.end(),
                       [x,y](int n) { return x < n && n < y; }
                      ),
             v.end()
           );

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;
    return 0;
}


3 6
0 1 2 3 6 7 8 9



We passed the x and y parameters by value. Basically it is equivalent:



struct LambdaFunctor
{
public:
    LambdaFunctor(int a, int b) : m_a(a), m_b(b) { }
    bool operator()(int n) const { return m_a < n && n < m_b; }
private:
    int m_a;
    int m_b;
};

   // ...
   v.erase( remove_if(v.begin(),v.end(),LambdaFunctor(x,y)), v.end());



The x and y parameters are copied and being stored in the function object.
We cannot modify the captured values because the operator() in functor is
const. It is a "real" copy, therefore the modification of x and y is not
reflected inside the lambda.


    // ...
    auto f = [=](int n) { auto sz = v.size(); return x < n && n < y; };
    cout << "size of f = " << sizeof(f) << endl;
    // ...

    size of f = 32



[=]    default-capture lambda introducer. Captures all locals by value.


Lambda expressions are const by default but we can manage to modify x and y.
Note, that modification of prev does not update local prev variable.



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

using namespace std;

int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    int prev = 0;
    for_each(v.begin(), v.end(), [=](int& r) mutable {
                            const int oldr = r;
                            r *= prev;
                            prev = oldr;
                         });

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << "prev = " << prev << endl;
    return 0;
}


0 0 2 6 12 20 30 42 56 72 prev = 0




Capture by reference



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

using namespace std;

int main()
{
    vector<int> v;
    for(int i = 0; i < 10; ++i)
        v.push_back(i);

    int prev = 0;

    // no need for mutable:
    // referenced values can be modified from const method 
    for_each(v.begin(), v.end(), [&prev](int& r)  {
                                    const int oldr = r;
                                    r *= prev;
                                    prev = oldr;
                             });

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << "prev = " << prev << endl;
    return 0;
}


0 0 2 6 12 20 30 42 56 72 prev = 9


[&]         Capture everything by reference

[=, &x, &y] Capture everything by value but x and y which capured by reference

[&, x, y]   Capture everything by reference but x and y which capured by value









References
==========



Bjarne Stroustrup: The C++ Programming Language (4th Edition)
Addison-Wesley Professional; 4 edition (May 19, 2013) ISBN-10 0321563840


Bartosz Milewski: Functional Data Structures in C++
(C++Now, Aspen, CO, US, 2015)
https://www.youtube.com/watch?v=OsB09djvfl4


Scott Meyers: Effective modern C++ (O'Reilly Media, 2014)
ISBN 978-1-4919-0399-5 | ISBN 10 1-4919-0399-6


Peter Sommerlad: C++14 Compile-time computation (ACCU 2015)
http://wiki.hsr.ch/PeterSommerlad/files/ACCU2015VariadicVariableTemplates.pdf