510 likes | 695 Views
C++11. Alejandro Álvarez & Andrea Valassi. White Area Lectures - April 19th, 2014. Overview. Previous standard was C++03 Bug-fixes over C++98 C++11 released on August 2011 C++14 already on its way bringing small improvements Previously known as C++0x You can expect
E N D
C++11 Alejandro Álvarez & Andrea Valassi White Area Lectures - April 19th, 2014
Overview • Previous standard was C++03 • Bug-fixes over C++98 • C++11 released on August 2011 • C++14 already on its way bringing small improvements • Previously known as C++0x • You can expect • A few small improvements that will make your code (even) nicer • A more powerful standard library that will make your life easier
Those “little” things Easier to write and to read
The space after the > C++03 vector<complex<double> > a; C++11 vector<complex<double>> a;
nullptr C++03 MyClass *ptr = NULL; C++11 MyClass *ptr = nullptr; nullptr is a pointer and only a pointer, so you can’t do int a = nullptr;
Range based for loops C++03 vector<int> myvector; vector<int>::const_iterator i; for (i = myvector.begin(); i != myvector.end(); ++i) { code here... }
Range based for loops C++11 vector<int> myvector; for (int i : myvector ) { code here... }
Even nicer C++03 vector<pair<string,double>> dict; vector<pair<string,double>>::const_iterator i; for (i = dict.begin(); i != dict.end(); ++i) { code here... }
Even nicer C++11, first round vector<pair<string,double>> dict; for (pair<string,double> i : dict) { code here... }
Even nicer C++11, second round vector<pair<string,double>> dict; for (auto i : dict) { code here... } The auto keyword can be used to let the compiler infer the type. Can not be used for function parameters.
Another example of auto C++03 std::vector<std::pair<std::string, ns::MyType>> x = my_function(); C++11 auto x = my_function();
Constructors can be inherited! classBase { public: Base(int x); Base(int x, int y); Base(double z); };
Constructors can be inherited! C++03 classDerived: publicBase { public: Derived(int x): Base(x); Derived(int x, int y): Base(x, y); Derived(double z): Base(z); };
Constructors can be inherited! C++11 classDerived: publicBase { public: usingBase::Base; };
Constructor delegation C++11 classBase { public: Base(); Base(int x): Base(x, 0); Base(int x, int y): Base(); Base(double z): Base(static_cast<int>(z)); };
Default initialization C++11 struct MyStruct { int field = 42; std::string str = “Hello there”; std::vector<int> v = {1, 2, 3, 4}; }; Here you can see a new concept: initializer list
Initializer list classMyClass { public: MyClass(std::initializer_list<int> v); } So you can do now MyClass a = {1, 2, 4, 20};
Default and delete classMyClass { public: MyClass() =default; MyClass(constMyClass& b) = delete; } defaultPredefined constructor deleteForbid usage
Final and override structDerived : publicBase { virtualvoiddoit(int, int, int) constoverridefinal; }; structReDerived : publicDerived { virtualvoiddoit(int, int, int) constoverride; }; That’s a compilation time error Note that this is an optional feature
Those “little” things • You have seen they make your code • Easier to write • Easier to read • Less boilerplate • Less repetitive • So use them!
The powerful features More for less
Lambdas! auto greater = [](int a, int b) { return a > b; }; greater(22, 33); // returns false The compiler figures out the return type (really!), but you can be explicit auto greater = [](int a, int b) -> bool { return a > b; };
Lambdas! (Closures) "A closure is essentially a stateful function that carries around with it a value or a reference to a variable defined outside the body of the function” You will see it clearer with the following example
Lambdas! (Closures) size_t val; auto callback = [&val] (size_t a) { val = a; }; func_with_callback(“hello”, callback); You can also capture by value [=val] You can capture everything[=] [&] You can capture this[this] Remember that the object life is not extended!!!!!
How do I receive a lambda? A lambda is a function, so, how do you receive a function? voidmy_func(std::function<int(double, double)> f); That means: f is a callable that receives two doubles and return an integer. Let it be a lambda, a functor, a function, ….
Applying lambdas in the STL Before std::vector<int> c{1,9,2,8,3,7,4,6,5}; int lo = 4;int hi = 7; usingstd::bind; usingstd::logical_and; usingstd::less; usingstd::greater_equal; usingnamespacestd::placeholders; auto i = find_if(begin(c), end(c), bind(logical_and<bool>(), bind(greater_equal<int>(), _1, lo), bind(less<int>(), _1, hi)));
Applying lambdas in the STL Before std::vector<int> c{1,9,2,8,3,7,4,6,5}; int lo = 4;int hi = 7; auto i2 = find_if(begin(c), end(c), [=](int i) { return i >= lo && i < hi;} );
Move semantics • This is one of the biggest improvements in C++11 • It is also one of the hardest to explain, let’s try with some code
Move semantics classVector { size_t size; T* array; };
Move semantics void swap(Vector& a, Vector& b){ Vector tmp = a; a = b; b = tmp; }; This operation is a waste of cycles tmp->array allocated and copied from a a->array freed, re-allocated, copied from b b->array freed, re-allocated, copied from tmp tmp->array freed
Move semantics • We could just move the pointer, but that won’t work in multiple cases • a = b • Do things with a AND b • However, it does in many others • a = b • Do things with a, forget of b • swap(a, b) • Other situations where the compiler can figure out • a = function(); where function returns b, and b falls out of scope
Move semantics • So, basically we have • Copies • Movements • But in C++98 • Only copy constructor and assignment operator exist • Now in C++11 • Copy constructor • Move constructor • Copy assignment operator • Move assignment operator
Move semantics classVector { size_t size; T* array; // Copy constructor Vector(constVector& o); // Move constructor Vector(Vector&& o); // Copy assignment operator Vector& operator = (constVector &); // Move assignment operator Vector& operator = (Vector &&); };
Move semantics Vector::Vector(constVector& o) { this->array = newT[o.size]; memcpy(this->array, o.array, o.size * sizeof(T)); } Vector::Vector(Vector&& o) { this->array = o.array; this->size = o.size; o.array = nullptr; o.size = 0; }
Move semantics After a move, the “moved” object is unusable (but destructible) until you re-assign something else void swap(Vector& a, Vector& b){ Vector tmp = std::move(a); a = std::move(b); b = std::move(tmp); }; Now, not a single allocation or freeing!
Move semantics • There is more trickery involved • Copy and swap semantics • Default move methods provided by compiler if members define move semantics (!) • Perfect forwarding • lvalues, prvalues, xvalues, gvalues, rvalues (arght!)
Move semantics But at least returning by value BigClass generateBigClass(); BigClass x = generateBigClass(); It is not expensive anymore (and no pointers flying around needed!)
Emplacements // Assume Big(int) constructor std::vector<Big> v; Big b(1); v.push_back(b); // Copy v.push_back(std::move(b)); // Move v.push_back(Big(2)); // Construct, Move v.push_back({3}); // Construct, Move v.emplace_back(4); // Construct in place Basically, it allows to construct in place elements that belong to a container
Talking about pointers • Smart pointers are in std::! • std::unique_ptr • Only one owner of the object, can not be copied, can be moved • std::shared_ptr • Several owners, can be copied, can be moved, when all are destroyed, the object is freed • std::weak_ptr • Observer of shared_ptr, does not own, but it is notified on release • std::auto_ptr • Deprecated!
Talking about pointers new and delete are to be avoid now! Smart pointers to handle lifetime, or classes that need to (i.e. vector) std::unique_ptr<Big> big_ptr{1}; Rather than std::unique_ptr<Big> big_ptr(new Big(1));
Few recommendations • Pass objects by value, reference or pointer • unless there is a change in ownership • Accept smart pointers by non-const only if it is going to be modified • Avoid copying shared_ptr (expensive!) • unique_ptr is a better choice, normally • Do NOT own bare pointers • Accepting unique_ptr by value means I will take ownership • Accepting shared_ptr by value means I will share ownership
Summary • C++11 makes coding easier and nicer • More efficient without the risk of free range pointers • Move semantics • And safer • Smart pointers
But wait, there is more! • Variadic templates • Concurrency • std::thread • std::async • std::promise • std::packaged_task • New algorithms • Hash tables • Tuples • Type traits • constexpr • New string literals • u8“UTF-8 String”
Awesome! Let’s switch to C++11! Not so fast...
Compiler support • gcc 4.8 has a complete support • gcc 4.1 in SL5 does not support it • gcc 4.4 in SL6 only partial support • No lambdas :( • clang 3.4 in SL6 supports it • No clang in SL5 • But…
Compatibility • If you are standalone, you are good • If you maintain a C++ library, forget about it • If you use a C++ library, forget about it • Linking clang and gcc binaries likely to break • Theoretically, the ABI between C++03 and C++11 are compatible • As long as you don’t use the STL! • Which is like saying: no, they are not • So you need to compile the whole stack in C++03 or C++11, no mixing
Current status Of our software stacks
DMLite • Is a C++ library used by C++ applications • Can only switch on -std=c++11/c++0x when we do so for the whole dependency chain • EPEL7? • Shame, because there are a couple of ugly constructs that would be way easier with C++11 • See variadic templates
FTS3 • Already compiling with -std=c++0x • Was written using C++98, so C++11 occurrences are rare (new code) • auto keyword is a bless!
Questions? C??