Modernes C++

Hier sind die Folien (+Bugfixes) von meinem Vortrag über modernes C++ auf der GPN14 als HTML-Dokument.

###C++

1983 - Bjarne Stroustrup bolts everything he’s ever heard of onto C to create C++. The resulting language is so complex that programs must be sent to the future to be compiled by the Skynet artificial intelligence. Build times suffer. Skynet’s motives for performing the service remain unclear but spokespeople from the future say “there is nothing to be concerned about, baby,” in an Austrian accented monotones. There is some speculation that Skynet is nothing more than a pretentious buffer overrun.

James Iry: A Brief, Incomplete, and Mostly Wrong History of Programming Languages

Grundprinzipien

###RAII

void safe_fun(std::string path) {
	path += "file.txt"; //can throw
	std::ifstream file{path};
	util::enforce_opened(file, path); //can throw
	// string-reallocation can throw:
	for (std::string line; getline(file, line);) {
		std::lock_guard<std::mutex> guard{global_mutex};
		global_vec.emplace_back(line); //can throw
	}
}

###Ownership

int main() {
	std::string str; // owner = main
	str += "foobar"; // changes by owner are fine
	fun(str); // main() stays owner, should only
	          // pass str by const reference
	// cleanup by main(), because it is the owner
}

###Low- vs. Highlevel

###Highlevel = schnell

###Bibliotheken

###Pass-by-Value vs. Pass-by-Reference


class my_class {
public:
	my_class(std::string str): str{std::move(str)} {}
	// my_class(const std::string& str): str{str} {}
	
private:
	std::string str;
};

int main() {
	my_class var1{"some pretty long string"};
	std::string str = "long string with many chars";
	my_class var2{str};
	my_class var3{std::move(str)};
}

###Metaprogrammierung

C++14

###Übersicht

###Literale

###Lambdas

[](auto a, auto b){return a+b;}
[ptr = std::move(ptr)] {return ptr->foo();}

###Funktionen

auto fun() {return 42;}
	constexpr unsigned exp(unsigned b, unsigned e) {
		unsigned returnval = 1;
		for(unsigned i = 0; i < e; ++i) {
			returnval *= b;
		}
		return returnval;
	}

###Templates

template<typename T> constexpr T pi = 3.14159;

###Sonstige Kernsprachenfeatures

[[deprecated("use new_good_function instead")]]
int* ancient_bad_function();

std::make_unique

// efficient thanks to perfect forwarding:
auto ptr = std::make_unique<std::string>("foo");
//spot the bug:
void f(std::unique_ptr<int>, std::unique_ptr<int>);

f(std::unique_ptr<int>(new int(3)),
  std::unique_ptr<int>(new int(4)));

f(std::make_unique<int>(3),
  std::make_unique<int>(4));

Benutzerdefinierte Literale für die Standard-Bibliothek

using namespace std::literals;

int main() {
	auto s = "foo"s; // std::string
	auto seconds = 3s; //std::chrono::seconds
}

Robustere Sequenz-Algorithmen

std::vector<int> vec1 = {1,2,3}, vec2 = {1,2,3,4};

// oops: returns true
std::equal(begin(vec1), end(vec1), begin(vec2));

// oops: UB
std::equal(begin(vec2), end(vec2), begin(vec1));

// correct: returns false
std::equal(begin(vec1), end(vec1),
           begin(vec2), end(vec2));

„Quoted“ Strings

std::stringstream ss;
std::string str;
ss << "foo bar";
ss >> str; // "foo"
ss << std::quoted("foo bar");
ss >> std::quoted(str); // "foo bar"

Tuple-Indexierung via Typ

using std::string;
std::tuple<string, string, int> t("foo", "bar", 7);

int i = get<int>(t); // i == 7
int j = get<2>(t); // Equivalent to the above: j == 7
string s = get<string>(t); // Compile-time error

std::exchange

template<typename T, typename U=T>
T exchange(T& obj, U&& new_val) {
	T old_val = std::move(obj);
	obj = std::forward<U>(new_val);
	return old_val;
}

The benefit isn’t huge, but neither is the specification cost.

Shared Locking

using namespace std::literals;
std::shared_timed_mutex mut;
using guard = std::shared_lock<std::shared_timed_mutex>;

void reader() {
	if(guard g{mut, 100us}){
		read();
	} else {
		panic();
	}
}

Concepts TS

Motivierendes Beispiel

#include <algorithm>
#include <list>

int main() {
	std::list<int> lst = {4, 3, 2, 1};
	sort(begin(lst), end(lst));
}

Clang und GCC sind empört!

Concepts

error: no matching call to ‘sort(forward_list<int>&)’
note: candidate is ‘sort(C& container)’
note:   where C = forward_list<int>
note: template constraints not satisfied
note:   ‘C’ is not a/an ‘Sortable’ type since
note:      ‘c[n]’ is not valid syntax

C++17

Übersicht

Idiomatische Verwendung

Rückgabewerte

void fun(int arg, int& ret1, int& ret2);
std::tuple<int, int> fun(int arg);
long val; // sic
std::tie(val, std::ignore) = fun(42);

Lexikographische Vergleiche

struct col {
	unsigned char r, g, b;
};

bool operator<(const col& l, const col& r) {
	return std::tie(l.r, l.g, l.b)
	     < std::tie(r.r, r.g, r.b);
}

Typinferenz

void fun1(const std::map<std::string, int>& map) {
	for(const std::pair<std::string, int>& pair: map) {
		std::cout << pair.first << ": "
		          << pair.second << '\n';
	}
}


void fun2(const std::map<std::string, int>& map) {
	for(auto&& pair: map) {
		std::cout << pair.first << ": "
		          << pair.second << '\n';
	}
}

Algorithmen

If you want to improve the code quality in your organization, replace all your coding guidelines with one goal:

No raw loops!

– Sean Parent

std::rotate(begin(container), sub_range_begin,
            sub_range_end);
std::stable_partition(begin, target, std::not1(pred));
std::stable_partition(target, end, pred);
std::vector vec(n - 3);
std::iota(begin(vec), end(vec), 3);

Argument-Dependent-Lookup

int main() {
	std::vector<int> vec;
	foo::bar x; // foo::begin(foo::bar) exists
	bla::blub y; // bla::begin(bla::blub) doesn't exist
	
	using std::begin;
	
	auto it1 = begin(vec); // -> std::begin
	auto it2 = begin(x);   // -> foo::begin
	auto it3 = begin(y);   // -> std::begin
}

Einsatz von Exceptions

Umgang mit Exceptions

int main() try {
	do_something();
} catch(std::runtime_error& e) {
	std::cerr << "Runtime-error: " << e.what() << '\n';
}

Type-Erasure

struct base {
	virtual std::unique_ptr<base> clone() const = 0;
	virtual ~base = default
};

template<typename T> class wrapper: base {
	T value;
	virtual std::unique_ptr<base> clone() const override {
		return std::make_unique<wrapper<T>>(value);
	}
};

Sonstiges

Probleme

Stärken

Antipatterns

std::endl

	std::cout << "bad" << std::endl;
	std::cout << "good\n";
	
	std::cerr << "NEVER what you want!" << std::endl;
	std::cerr << "since std::cerr is unbuffered.\n";

Spezielle Methoden

//foo.hpp:
class foo {
	foo();
	virtual ~foo();
	// stuff
};

//foo.cpp
foo::foo() {}
foo::~foo() {}

Spezielle Methoden

// better: Rule of 5
class foo {
	foo() = default;
	virtual ~foo() = default;
	foo(const foo&) = default;
	foo(foo&&) = default;
	foo& operator(const foo&) = default;
	foo& operator(foo&&) = default;
	// stuff
};
// usually best: Rule of zero
class foo{
	// stuff
};

Klassenmissbrauch (1)

class math {
	static double sin(double);
	static double cos(double);
	static double tan(double);
};
int main() {
	math m; // err...
	auto m2 = m; // You can copy a math?
	assert(&m != nullptr); // math has an adress?
}

Klassenmissbrauch (2)

class searcher {
	std::size_t search(const std::string& haystack,
	                   char needle) const {
		/* ... */
	}
};
std::size_t search(const std::string& haystack,
                   char needle) { /* ... */ }