Slides by Ben / Code samples by Coliru / Layout by reveal.js
Every example that follows compiles with
>> illegal in C++98
int main()
{
std::vector<std::vector<std::string> > vec;
// Now you can write:
std::vector<std::vector<std::string>> vec2;
}
All about type inference
Syntax:
auto variable initializer
auto function -> return type
Specifies that the type of the variable that is being declared will be automatically deduced from its initializer.
auto i = 42;
auto u = 1729U;
auto str = std::string("Mehr Bier");
If you are lazy
struct Beer
{
Beer(const char* s) : name(s) {}
std::string name;
};
int main()
{
std::vector<Beer> vec{ "Thier", "Bergmann", "Kronen" };
for (std::vector<Beer>::iterator it = vec.begin(); it != vec.end(); ++it)
{
std::cout << it->name << "-Pils\n";
}
}
If you want to avoid bugs
char buf[20];
std::istringstream stream("Hello, there!");
stream.read(buf, sizeof buf);
// Conversion from std::streamsize to size_t
size_t bytes_extracted = stream.gcount();
std::cout << "Characters extracted " << bytes_extracted << '\n';
template<class T, class U>
auto add(T t, U u) -> decltype(t + u)
{
return t + u;
}
All about uniform initialization
{ }
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
int main()
{
std::vector<double> vec{ 1, 2, 3.456, 99.99 };
std::unordered_map<std::string, std::string> beers{
{"Thier","Dortmund"},
{"Krombacher","Kreuztal"},
{"Fiege","Bochum"}
};
for (const auto& beer : beers)
std::cout << beer.first << ", " << beer.second << std::endl;
}
Supported since November 2012 CTP
#include <initializer_list>
template<class T> class ListJ
{
public:
// initializer-list constructor
ListJ (std::initializer_list<T> s)
{
// get the right amount of space
reserve(s.size());
// initialize elements
uninitialized_copy(s.begin(), s.end(), elem);
// set size
sz = s.size();
}
// ...
};
Iterate through a range
which is anything with a begin() and end()
or with a begin() and length
int main()
{
std::vector<std::string> vec{"Bergmann", "Union Export", "Fiege"};
for (auto& beer : vec) // access as reference to change a value
{
std::for_each(begin(beer), end(beer),
[](char& c) { c = std::toupper(c); });
}
// access as const-ref to avoid copying
for (const auto& beer : vec) std::cout << beer << '\n';
for (auto x : { 11,22,33,44,55,66,77 }) std::cout << x << '\n';
}
An anonymous function (a function without a name, thats all)
Can have an optional capture clause (that makes it a closure)
Syntax:
[capture-list] (params) {body}
[capture-list] {body}
[capture-list] (params) -> ret {body}
[capture-list] (params) mutable {body}
int main()
{
std::vector<int> vec{ 1,2,3,4,5,6,7 };
int x = 4;
vec.erase(std::remove_if(vec.begin(), vec.end(),
[x](int n) { return n < x; } ), vec.end());
std::cout << "vec: ";
for (auto i: vec) {
std::cout << i << ' ';
}
std::cout << '\n';
// The type of a closure cannot be named, but can
// be inferred with auto
auto add4 = [](int i) { return i+4; };
std::cout << "add4: " << add4(6) << '\n';
}
Access variables from outer scope (and optionally modify them)
#include <vector>
#include <iostream>
#include <string>
int main()
{
std::vector<std::string> vec{ "Dortmunder Actien-Brauerei" };
auto push_to_vec = [](const std::string& beer)
{
vec.push_back(beer);
};
for (const auto& b : { "Hansa", "Bergmann", "Thier" })
{
push_to_vec(b);
}
for (const auto& b : vec) std::cout << b << '\n';
}
capture-list:
[a,&b] a is captured by value, b is captured by ref
[this] captures this pointer by value
[&] captures all variables used in body of lambda by ref
[=] captures all variables used in body of lambda by value
[] captures nothing
Now on to the quest on getting rid of owning raw-pointers: Smart pointers!
new, delete
new[], delete[]
#include <memory>
struct Foo
{
Foo() { std::cout << "Foo::Foo\n"; }
};
int main()
{
std::unique_ptr<Foo> p1(new Foo); // p1 owns Foo
// Foo instance is destroyed when p1 goes out of scope
}
#include <memory>
int main()
{
{
std::shared_ptr<int> p1(new int); // count is 1
{
std::shared_ptr<int> p2(p1); // count is 2
{
std::shared_ptr<int> p3(p1); // count is 3
} // count goes back down to 2
} // count goes back down to 1
} // here the count goes to 0 and the int is deleted.
}
Hash tables, yeah :/
#include <unordered_map>
template<
class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;
Control of defaults
Remember?
class Foo
{
public:
// ...
private:
// Never defined
Foo(const Foo&);
Foo& operator=(const Foo&);
};
Now you can just write:
class Foo
{
public:
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
// ...
};
We can delete an undesired conversion like this:
struct Foo
{
// ...
Foo(long long); // can initialize with an long long
Foo(long) = delete; // but not anything less!
};
const char* str = R"("quoted string")";
// the string is "quoted string"
Very useful if you have inline SQL or regexp.
Motivating example:
#include <iostream>
#include <regex>
int main()
{
const char* pat = R"((\+|-)?[[:digit:]]+)";
std::regex integer_pattern(pat);
const char* input = "-123";
std::cout << (std::regex_match(input, integer_pattern) ?
"matches" : "does not match") << std::endl;
}
A null pointer literal
Yes, it has a type: std::nullptr_t
Contrary:
#define NULL 0
Actually, implementation defined, but pretty likely
void f(int* pi) { }
void f(double* pd) { }
void f(std::nullptr_t nullp) { }
int main()
{
int* pi; double* pd;
f(pi);
f(pd);
f(nullptr); // would be ambiguous without void f(nullptr_t)
// f(NULL); // ambiguous overload
}
Pretty easy to transform your code base, too ;)
$ sed -i `s/NULL/nullptr/g` *.{h,cpp}