Cserép Máté
I am a researcher and lecturer in computer science with several years of experience in object-oriented design and development. I have an application development experience primarily in C++, the C#/.NET technologies, PHP and Python.
Máté Cserép
October 2015, Budapest
template<class T> void printall(const vector<T>& v)
{
for (typename vector<T>::const_iterator p = v.begin(); p!=v.end(); ++p)
cout << *p << "\n";
}
template<class T> void printall(const vector<T>& v)
{
for (auto p = v.begin(); p!=v.end(); ++p)
cout << *p << "\n";
}
When the type of a variable depends critically on template argument it can be really hard to write code without auto.
template<class T, class U> void (const vector<T>& vt, const vector<U>& vu)
{
// ...
auto tmp = vt[i]*vu[i];
// ...
}
void f(const vector<int>& a, vector<float>& b)
{
typedef decltype(a[0]*b[0]) Tmp;
for (int i=0; i<b.size(); ++i) {
Tmp* p = new Tmp(a[i]*b[i]);
/* ... */
}
/* ... */
}
We can deduce the type of an expression:
std::intitializer_list<int> il = { 1, 3, 5, 1, 76, 8 };
for (int *p = il.begin(); p != il.end(); ++p)
{
cout << *p << endl;
}
Arrays and the standard containers support reading data from initializer lists:
int t[] = { 4, 3, 6, 1 };
vector<double> v = { 1, 2, 3.456, 99.99 };
list<pair<string,string>> languages = {
{"Nygaard","Simula"}, {"Richards","BCPL"}, {"Ritchie","C"}
};
map<vector<string>,vector<int>> years = {
{ {"Maurice","Vincent", "Wilkes"},{1913, 1945, 1951, 1967, 2000} },
{ {"Martin", "Ritchards"} {1982, 2003, 2007} },
{ {"David", "John", "Wheeler"}, {1927, 1947, 1951, 2004} }
};
void f(const vector<double>& v)
{
for (auto x : v) cout << x << '\n';
for (auto& x : v) ++x; // using a reference to allow us to change the value
}
int x[] = { 1, 2, 4, 6};
for(int elem : x)
{
cout << x << '\n';
}
You can iterate through anything like an STL-sequence defined by a begin() and end(). All standard containers can be used as a range, as can a string, an initializer list, an array, and anything for which you define begin() and end(), e.g. an istream.
The begin() and end() can be a member or a free-standing function to be called like x.begin() or like begin(x) (e.g. for arrays).
enum Alert { green, yellow, election, red };
enum class Color { red, blue };
Traditional enum:
It would be nice to have a:
enum Alert { green, yellow, election, red };
enum class TrafficLight { red, yellow, green };
Alert a = 7; // error (as ever in C++)
Color c = 7; // error: no int->Color conversion
int a2 = red; // ok: Alert->int conversion
int a3 = Alert::red; // error in C++98; ok in C++11
int a4 = blue; // error: blue not in scope
int a5 = Color::blue; // error: not Color->int conversion
Color a6 = Color::blue; // ok
Conversion:
enum class Color : char { red, blue }; // compact representation
enum class TrafficLight { red, yellow, green }; // by default, the underlying type is int
enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U }; // how big is an E?
// (whatever the old rules say;
// i.e. "implementation defined")
Specify the representation:
enum class Color_code : char; // (forward) declaration
void foobar(Color_code* p); // use of forward declaration
// ... later ...
enum class Color_code : char { red, yellow, green, blue }; // definition
Support of forward declaration:
enum class Permission
{
Execute = 1,
Write = 2,
Read = 4
};
Permission p = Permission::Execute | Permission::Write;
if(p & Permission::Read)
{
// ... work ...
}
Usage as flags (plain enums also support):
const int x = 10;
class Test;
static_assert(x >= sizeof(Test), "hiba");
Compile time assertion of constant expressions:
int f(int* p, int n)
{
static_assert(p == 0 ,"p is not null"); // error: static_assert() expression not a constant expression
/* ... */
}
template <class T>
struct data_structure
{
static_assert(std::is_default_constructible<T>::value,
"Data Structure requires default-constructible elements");
};
struct no_default
{
no_default() = delete;
};
int main()
{
data_structure<int> ds_ok;
data_structure<no_default> ds_error; // compilation error
}
Check constructor existence:
template <class T>
void swap(T& a, T& b) {
static_assert(std::is_copy_constructible<T>::value,
"Swap requires copying");
static_assert(std::is_move_constructible<T>::value &&
std::is_move_assignable<T>::value &&
(!std::is_nothrow_move_constructible<T>::value ||
!std::is_nothrow_move_assignable<T>::value),
"Swap may throw");
auto c = b;
b = a;
a = c;
}
struct no_copy {
no_copy ( const no_copy& ) = delete;
no_copy () = default;
};
int main() {
int a, b;
swap(a, b);
no_copy nc_a, nc_b;
swap(nc_a, nc_b); // compilation error
}
Check proper move semantics support:
#include <iostream>
#include <complex>
using namespace std;
int main()
{
complex<double> a(4, 2);
a = 8;
complex<double> c = 6.0i;
cout << c << endl;
}
Motivation:
constexpr complex<double> operator "" i(long double d) // imaginary literal
{
return {0,d}; // complex is a literal type
}
Solution:
User defined literals should always be prefixed with _ (underscore)
E.g. this literal will ship in C++14
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
auto lamda = [](int n) { cout << n << " "; };
lamda(10);
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
cout << endl;
return 0;
[] lambda-introducer
(int n) lambda parameter declaration
{ ... } compound statement
struct LambdaFunctor
{
void operator() (int n) const { cout << n << " "; }
};
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;
}
Lambda expressions define classes and construct objects.
The previous program is equivalent to:
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
for_each(v.begin(), v.end(),
[](int n)
{
cout << n ;
if ( n % 2 )
cout << ":odd ";
else
cout << ":even ";
});
cout << endl;
A lamda expression can contain multiply statements:
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
deque<int> d;
transform(v.begin(), v.end(), front_inserter(d), [](int n) {return n*n;} );
for_each(d.begin(), d.end(), [](int n) { cout << n << " "; });
cout << endl;
If the lambda expression has return value, than the return type is automatically deduced from that expression.
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
deque<double> d;
transform(v.begin(), v.end(), front_inserter(d),
[](int n) -> double { return n / 2.0; } );
for_each(d.begin(), d.end(), [](double n) { cout << n << " "; });
cout << endl;
Sometimes it is not easy to deduce the return type or not the desire type is deduced. We can explicitly specify the return type:
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
int x = 0;
int y = 0;
int z = 0;
cin >> x >> y;
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// [0, 1, 2, 8, 9, 3, 4, 5, 6, 7]
auto lamda = [x,y](int n) { return x < n && n < y; };
cout << sizeof(lamda) << endl;
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;
The [] lamda specifier means, that the lambda is stateless, but we can capture local variables.
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;
};
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
cin >> x >> y;
int x, y;
v.erase(remove_if(v.begin(),v.end(), LambdaFunctor(x,y)), v.end());
return 0;
}
Since we passed the x and y parameters by value, it is basically equivalent to the following:
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(),
[=](int n) { return x < n && n < y; }),
v.end()
);
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
cout << endl;
return 0;
The [=] is the default-capture lambda introducer.
Captures all locals by value.
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i * 2);
const int prev = 0;
for_each(v.begin(), v.end(),
[&prev](int& r)
{
cout << "(" << prev << "," << r << ")" << endl;
prev = r;
});
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
cout << "prev = " << prev << endl;
How can we capture by reference?
Examples:
[&] Capture everything by reference
// [=, &x, &y] Capture everything by value but x and y which by reference
// [&, x, y] Capture everything by reference but x and y which is by value
struct X {
int s;
vector<int> v;
void print() const {
for_each(v.begin(), v.end(), [this](int n) { cout << n*s << " "; });
}
};
int main() {
X x;
x.s = 2;
for(int i = 0; i < 10; ++i)
x.v.push_back(i);
x.print();
return 0;
}
Capturing the current object in a method:
int ii = 5;
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
int x = 0;
int y = 10;
auto f = [x, y](int n) { if (x < n-ii && n-ii < y)
cout << n << " ";
};
for_each(v.begin(), v.end(), f);
cout << endl;
ii = 1;
for_each(v.begin(), v.end(), f);
cout << endl;
return 0;
}
Global variables and static data members could be used, but they are not captured!
void doit(const vector<int>& v, const function<void (int)>& f)
{
f(10);
for_each(v.begin(), v.end(), f);
cout << endl;
}
int main()
{
vector<int> v;
int i = 0;
generate_n(back_inserter(v), 10, [&] { return i++; } );
doit(v, [](int n) { cout << n << " "; });
const function<void (int)>& ff = [](int n) { cout << n << " "; };
doit(v, ff);
return 0;
}
What is the type of a lamda function object?
std::function
By Cserép Máté
I am a researcher and lecturer in computer science with several years of experience in object-oriented design and development. I have an application development experience primarily in C++, the C#/.NET technologies, PHP and Python.