The
With
C++11
Richard Bateman
OpenWest 2015
Presentation Goal:
Discuss new C++11 features
This presentation is not exhaustive!
nullptr
// Old way
MyClass* classPtr = NULL;
// New way
MyClass* classPtr = nullptr;
What's the difference?
nullptr is not an int!
bool isCorrect(int a) {
// Return true if a is between -20 and +20 inclusive
return a >= -20 || a <= 20;
}
bool isCorrect(const char* myStr) {
// Return true if the pointer is non-null
return !myStr;
}
isCorrect(15);
true
isCorrect(-25);
false
isCorrect(NULL);
true
isCorrect(nullptr);
false
char* e = NULL;
isCorrect(e);
false
// Create a type with typedef
typedef std::map<std::string, int> IntMap;
// Create the same thing with using
using IntMap = std::map<std::string, int>;
Basic example:
Cute... but why??
// With typedef
template <typename T>
struct StringMap {
typedef std::map<std::string, T> type;
};
StringMap<int>::type mytype;
Making a string-indexed map type:
// With using
template <typename T>
using StringType = std::map<std::string, T>;
StringMap<int> mytype;
std::string lvalString = "Stuff";
callFunction("Stuff");
callFunction(lvalString);
lvalString = std::string("More stuff!");
lvalues
rvalues
Can you identify all of the lvalues and rvalues?
#include <iostream>
using std::cout; using std::endl; using std::string;
void printString(string& myString) {
cout << "lRef string: " << myString << endl;
}
void printString(string&& myString) {
cout << "rRef string: " << myString << endl;
}
int main() {
printString("Test string");
printString(std::string("another test string"));
std::string lvStr("lval String");
printString(lvStr);
return 0;
}
$ g++ -std=c++11 -o test testFile.cpp ; ./test
rRef string: Test string
rRef string: another test string
lRef string: lval String
What will the output be?
std::move turns a lvalue into a rvalue
#include <utility>
move constructors and move assignments use rvalue references
A move constructor or move assignment moves the memory from the old object to the new object, invalidating the old
std::string a("foo");
std::string b = std::move(a);
// a will be invalid! Don't do this! =]
auto
auto a = 5; // int
auto b = 3.5; // double
auto c = 3.5f; // float
auto d = "Something"; // const char*
Cute... but why??
template <typename T>
int theyShouldPayMeMore(
const std::map< std::string, std::pair<int, T>> inMap ) {
for (std::map< std::string, std::pair<int, T>>::const_iterator
it = inMap.begin();
it != inMap.end(); ++it) {
std::pair<int, T> curPair = inMap->second;
callFn(pair.second);
}
}
Now with auto
template <typename T>
int theyShouldPayMeMore(
const std::map< std::string, std::pair<int, T>> inMap ) {
for (auto it = inMap.begin(); it != inMap.end(); ++it) {
auto curPair = inMap->second;
callFn(pair.second);
}
}
template <typename T>
int theyShouldPayMeMore(
const std::map< std::string, std::pair<int, T>> inMap ) {
for (auto cur : inMap) {
auto curPair = inMap.second;
callFn(pair.second);
}
}
And just to clean it up, add using
template <typename T>
using IntPairMap<T> = std::map< std::string, std::pair<int, T> >;
template <typename T>
int theyShouldPayMeMore(const IntPairMap<T> inMap ) {
for (auto cur : inMap) {
auto curPair = inMap.second;
callFn(pair.second);
}
}
for (auto cur : intVector) {
callFn(cur);
}
decltype
// Make an int
auto a = 5;
// Make another int! (and a compiler warning)
decltype(a) b = 3.5;
Cute... but why??
vector<string> a{"3", ".", "1", "4"};
auto a = 0; // a is an int
auto b = a.size(); // b is size_t
decltype(a.size()) c = 0; // c is size_t
for (decltype(a.size()) i = 0; i < a.size(); ++i) {
// If I'd used auto it wouldn't be the same type!
}
const int a = 42; // const int
auto b = a; // int (drops const)
decltype(a) c = a; // const int
lambda expressions, std::bind and std::function
The output of a lambda expression is a special unnamed temporary ClosureType object
Rules for lambdas:
#include <algorithm>
int countMatches(const vector<int>& haystack, int needle) {
auto countFilter = [needle](int cur) -> bool {
return cur == needle;
};
return std::count_if(haystack.begin(),
haystack.end(),
countFilter);
}
auto closure = [](arg1, arg2, ...) -> retType {
// Function body
};
[ ] stores the Capture List:
std::function<int(int)> getAdder(int lh) {
// Return a function that adds lh to its argument
return [&lh](int rh) { return lh+rh; };
}
auto fn = getAdd(5);
cout "Added value: " << fn(3) << endl;
FAIL
Never capture by reference a variable that may not be around when you call the closure!
returns a callable object of unspecified type which wraps an existing callable type
// Needed for _1, _2, _3, etc
using namespace std::placeholders;
std::vector<int> intVec;
auto addToVec = std::bind(&(std::vector<int>::push_back), &intVec, _1);
addToVec(3);
addToVec(4);
#include <functional>
Functor (noun):
An instance of a C++ class with the operator() overloaded so that it can be called like a function
std::function is a templated functor class which wraps any callable target -- functions pointers, lambda expressions, or other functors.
#include <functional>
using namespace std::placeholders; // for _1, _2, _3, ...
using MessageHandler = std::function<bool(int,std::string)>;
bool any(vector<MessageHandler> fnList, int p1, std::string p2) {
auto result = false;
for (auto fn : fnList) {
result |= fn(p1, p2);
}
return result;
}
bool numIsPositive(int p1, std::string p2) {
return p1 > 0;
}
auto evenSize = [](int p1, std::string p2) -> bool {
return (p1 + p2.size()) % 2;
};
MessageHandler numIsPositiveAlt
= std::bind(numIsPositive, _1, _2);
vector<MessageHandler> handlers
= {&numIsPositive, evenSize, numIsPositiveAlt};
cout << std::boolalpha << any(handlers, 0, "") << endl;
cout << std::boolalpha << any(handlers, 1, "") << endl;
cout << std::boolalpha << any(handlers, -1, "") << endl;
// Creates a two element array of std::string objects
string a[] = { "awesome", "presentation" };
// Creates a string "great presenter"
string b("great presenter");
// Creates ints
int c = 5;
int d(5);
Initializing old-style:
// Method 1
vector<string> a;
a.push_back("awesome");
a.push_back("presentation");
// Method 2
vector<string> a(2)
a[0] = "awesome";
a[1] = "presentation";
// With boost list_of
vector<string> a = boost::assign::list_of("awesome")("presentation");
Initializing a vector of strings:
// Creates a two element array of std::string objects
string a[]{ "awesome", "presentation" };
// Creates a string "great presenter"
string b{"great presenter"};
// Creates an int
int c{5};
Initializing new-style:
// Vector
vector<string> a{ "awesome", "presentation" };
// Map
map<string, int> b{ { "Presenter", "Richard" }, { "Class", "Awesome" } };
Initializing STL containers:
Caveat
// Creates a vector of size 2 with both values empty
vector<string> a(2);
// Creates a vector of size 1 with value 2
vector<string> a{2};
Why not use raw pointers?
3 new pointer types:
std::unique_ptr
#include <memory>
std::unique_ptr<MyObject> makeObject() {
std::unique_ptr<MyObject> objPtr = new MyObject();
objPtr->WriteMyPresentationForMe();
objPtr->DoOtherStuff();
objPtr->FinishGettingReady();
return objPtr;
}
{
auto ptr = makeObject();
ptr->PresentStuff();
}
std::shared_ptr
#include <memory>
auto a = std::make_shared<MyObject>(1, 2, 3);
// a is type std::shared_ptr<MyObject>
auto b = a;
auto c = b;
a.reset();
b.reset();
a=c;
b=a;
a.reset();
b.reset();
c.reset(); // destructor is called here
std::weak_ptr
#include <memory>
void testPtr(std::weak_ptr<MyObject> ptr) {
if (auto tmp = ptr.lock()) {
cout << "Pointer is good";
} else {
cout << "Pointer expired";
}
}
auto a = std::make_shared<MyObject>(1, 2, 3);
// a is type std::shared_ptr<MyObject>
auto b = std::weak_ptr<MyObject>(a);
testPtr(c); // Pointer is good
testPtr(b); // Pointer is good
testPtr(a); // Pointer is good
a.reset();
testPtr(b); // Pointer expired
testPtr(a); // Pointer expired
We don't have time to list all changes
Here are a few more quick ones
// Traditional enum
enum Color {RED, GREEN, ORANGE, YELLOW};
enum Fruit {GRAPE, ORANGE, APPLE, PEAR};
// This won't compile! ORANGE conflicts
// enum classes
enum class Color {RED, GREEN, ORANGE, YELLOW};
enum class Fruit {GRAPE, ORANGE, APPLE, PEAR};
// strongly-typed enum
enum class Errors : uint32_t { BROKEN, UGLY, SAD };
// To use enum classes:
Color a = Color::RED;
Fruit b = Fruit::ORANGE;
Applies to:
struct noncopyable
{
noncopyable() {};
private:
noncopyable(const noncopyable&);
noncopyable& operator=(const noncopyable&);
};
The old way:
struct noncopyable
{
noncopyable() =default;
noncopyable(const noncopyable&) =delete;
noncopyable& operator=(const noncopyable&) =delete;
};
The new way:
= delete explicitly deletes the function
= default explicitly uses the default generated function
class Foo {
public:
Foo() : Foo(0) {}
Foo(int i) : Foo(i, 0) {}
Foo(int i, int j) : num1(i), num2(j) {
average = (num1+num2) / 2;
}
private:
int num1;
int num2;
int average;
};