Sei sulla pagina 1di 49

C++ 11 Part 1

C++ 11
Part 1

C++ 11 Part 1

Abstract
C++11 (formerly known as C++0x) is the most recent version
of the standard of the C++ programming language.
It was approved by ISO on 12 August 2011, replacing C++03.
C++11 includes several additions to the core language and
extends the C++ standard library

C++ 11 Part 1

Explicit overrides

In C++03, it is possible to accidentally create a new virtual function, when one


intended to override a base class function. For example:
struct Base {
virtual void some_func(float);
};
struct Derived : Base {
virtual void some_func(int);
};

Suppose the Derived::some_func is intended to replace the base class version. But
instead, because it has a different signature, it creates a second virtual function.

This is a common problem, particularly when a user goes to modify the base class.

C++ 11 Part 1

Explicit overrides

C++11 provides syntax to solve this problem.


struct Base {
virtual void some_func(float);
};
struct Derived : Base {
virtual void some_func(int) override;
// ill-formed - doesn't override a base class method
};

The override special identifier means that the compiler will check the base
class(es) to see if there is a virtual function with this exact signature. And if there is
not, the compiler will indicate an error.

C++ 11 Part 1

final

Prevent inheriting from classes


Preventing overriding methods in derived classes

struct Base1 final { };

struct Derived1 : Base1 { };


// ill-formed because the class Base1 has been marked final
struct Base2 {
virtual void f() final;
};
struct Derived2 : Base2 {
void f(); // ill-formed because the virtual function Base2::f has been marked final
};

C++ 11 Part 1

Range-based for statements


In C++03
for (std::vector<int>::iterator itr = myvector.begin(); itr != myvector.end(); ++itr)

In C++11, the auto keyword makes this a little better


for (auto itr = myvector.begin(); itr != myvector.end(); ++itr)
C++11, range-based for statement (or sometimes called for each)
for (auto x: myvector) // x is read-only
{
cout << x;
}

C++ 11 Part 1

Range-based for statements


If you want to modify the value of x, you can make x a
reference
for (auto& x: myvector) // x is modifiable
{
cout << ++x;
}
This syntax works for C-style arrays and anything that supports
an iterator via begin() and end() functions.
This includes initialization_list

C++ 11 Part 1

static_assert
C++03 provides an assert macro that allows testing for
assertions at runtime.
However, for templated programming, its sometimes useful
to be able to test assertions at compile type.
C++11 provides a new keyword called static_assert that does
a compile-time assertion test.
static_assert(sizeof(int) >= 4, "int needs to be 4 bytes to use this code");

C++ 11 Part 1

static_assert
static_assert is checked at compile time, it can not be used to
evaluate assumptions that depend on runtime values.
static_asserts is primarily useful for checking the size of things
via sizeof() or determining that #defined values are within
certain boundaries.

static_assert(__cplusplus > 199711L, "Program requires C++11 capable compiler");

C++ 11 Part 1

Null pointer constant


void foo(char *);
void foo(int);
foo(NULL);

// will call foo(int)

C++11 provides a null pointer constant: nullptr. It is of type nullptr_t,


which is implicitly convertible and comparable to any pointer type

It is not implicitly convertible or comparable to integral types, except


for bool.
For backwards compatibility reasons, 0 remains a valid null pointer
constant.

C++ 11 Part 1

Null pointer constant


char *pc = nullptr; // OK
int *pi = nullptr; // OK
bool b = nullptr; // OK. b is false.
int i = nullptr;

// error

foo(nullptr);

// calls foo(nullptr_t), not foo(int);

In standard types headers for C++11, the nullptr_t


declared as:
typedef decltype(nullptr) nullptr_t;

type should be

C++ 11 Part 1

State the output


template<class F, class A>
void Fwd(F f, A a)
{
f(a);
}
void g(int* i) {
std::cout << "Function g called\n";
}
int main()
{
g(NULL);
g(0);
g(nullptr);
Fwd(g, nullptr);
Fwd(g, NULL);
}

C++ 11 Part 1

Right angle brackets


vector<vector<int> > vector_of_int_vectors;
You had to write a space between the two closing angle
brackets.
You no longer need to worry about it!
vector<vector<int>> vector_of_int_vectors;

C++ 11 Part 1

Type long long int


In C++03, the largest integer type is long int.
It is guaranteed to have at least as many usable bits as int.

This resulted in long int having size of 64 bits on some popular


implementations and 32 bits on others.
C++11 adds a new integer type long long int to address this
issue.
It is guaranteed to be at least as large as a long int, and have no fewer
than 64 bits.

The type was originally introduced by C99 to the standard C,


and most C++ compilers support it as an extension already

C++ 11 Part 1

Converting Constructor

A constructor declared without the function-specifier explicit specifies a conversion


from the types of its parameters to the type of its class.
struct foo
{
foo(int x);
foo(char* s, int x = 0);
foo(float f, int x);
explicit foo(char x);
};
foo bar(foo f)
{
return {1.0f, 5};
}

C++ 11 Part 1

Koenig lookup

Koenig Lookup is also commonly known as Argument Dependent Lookup in C++


You dont have to qualify the namespace for functions if one or more argument types
are defined in the namespace of the function.
namespace MyNamespace {
class MyClass {};
void doSomething(MyClass);
}
MyNamespace::MyClass obj; // global object
int main() {
doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
}

The compiler correctly identifies the unqualified name doSomething() as the


function declared in namespace MyNamespace by applying the Koenig algorithm.
The algorithm tells the compiler to not just look at local scope, but also the
namespaces that contain the argument's type.

C++ 11 Part 1

lvalues and rvalues


int a;
a = 1; // here, a is an lvalue
-----------------------------------------------------------int x;
int& getRef ()
{
return x;
}
getRef() = 4;
------------------------------------------------------------rvalue
int x;
int getVal ()
{
return x;
}
getVal();

C++ 11 Part 1

rvalues
string getName ()
{
return "Alex";
}
string name = getName();
getName() is an rvalue.
But you're assigning from a temporary object, not from some value that has a fixed
location.
Prior to C++11, if you had a temporary object, you could use a "regular" or "lvalue
reference" to bind it, but only if it was const
const string& name = getName(); // ok
string& name = getName(); // NOT ok

C++ 11 Part 1

rvalues
In C++11, however, there's a new kind of reference, an "rvalue
reference
string&& name = getName();
See Example1.cpp
Now we have a way to determine if a reference variable refers to
a temporary object or to a permanent object.
The rvalue reference version of the method is helpful if you use a
temporary object

C++ 11 Part 1

Move constructor
rvalue references are useful to create a move constructor and
move assignment operator
See Example2.cpp
Apply the move constructor here to avoid the expense of a deep
copy by providing the rvalue reference.
Avoid a copy by changing the original, temporary object!
See Solution2.cpp

C++ 11 Part 1

Move constructor
If you have a function that returns a const object, it will
cause the copy constructor to run instead of the move
constructor
const ArrayWrapper getArrayWrapper ();
// makes the move constructor useless,
// the temporary is const!
See Example3.cpp
Find the bug and provide a fix

C++ 11 Part 1

Move constructor
// move constructor
ArrayWrapper (ArrayWrapper&& other)
: _p_vals( other._p_vals )
, _metadata( other._metadata ) // incorrect
{
other._p_vals = NULL;
other._size = 0;
}

std::move
turns an lvalue into an rvalue
Use <utility>

C++ 11 Part 1

std::move
See Solution3.cpp
// move constructor
MetaData (MetaData&& other)
: _name( std::move( other._name ) )
, _size( other._size )
{}
// move constructor
ArrayWrapper (ArrayWrapper&& other)
: _p_vals( other._p_vals )
, _metadata( std::move( other._metadata ) )
{
other._p_vals = NULL;
other._size = 0;
}

C++ 11 Part 1

Move constructors and implicitly generated


constructors
When you declare any constructor, the compiler will no longer
generate the default constructor for you.
Adding a move constructor to a class will require you to
declare and define your own default constructor.
Declaring a move constructor does not prevent the compiler
from providing an implicitly generated copy constructor
Declaring a move assignment operator does not inhibit the
creation of a standard assignment operator.

C++ 11 Part 1

Initializer lists

C++03 has partial support for initializer lists, allowing you to use initializer lists for
simple aggregate data types - structs and C-style arrays
struct Employee
{
int nID;
int nAge;
float fWage;
};
Employee sJoe = {1, 42, 60000.0f};
int anArray[5] = { 3, 2, 7, 5, 8 };

However, this doesnt work for classes


int anArray[5] = { 3, 2, 7, 5, 8 }; // ok
std::vector<int> vArray[5] = {3, 2, 7, 5, 8}; // not okay in C++03

C++ 11 Part 1

Initializer lists

C++11 extends initializer lists so they can be used for all classes.

This is done via a new template class called std::initializer_list

If you use an initializer list on a class, C++11 will look for a constructor with a
parameter of type std::initializer_list.

std::vector<int> vArray[5] = {3, 2, 7, 5, 8};

// calls constructor std::vector<int>(std::initializer_list<int>);


#include <initializer_list>
See Example4.cpp
Apply the initializer list

C++ 11 Part 1

Initializer lists
See Solution4.cpp
std::vector has an initializer_list constructor in C++11
MyArray(const std::initializer_list<T>& x): m_Array(x)
// let vector constructor handle population of mArray
{
}

C++ 11 Part 1

Initializer lists

initializer_list has iterator functions begin() and end()


You can also use initializer lists a function parameters, and access the elements in
the same way
int sum(const initializer_list<int> &il)
{
int nSum = 0;
for (auto x: il)
nSum += x;
return nsum;
}
cout << sum( { 3, 4, 6, 9 } );

C++ 11 Part 1

Uniform initialization
initializer lists
type variable = { data, elements };
The uniform initialization syntax takes the following form:
type variable { data, elements }; // note: no assignment operator
This style of initialization will work for both plain aggregate data types
(structs and C-style arrays) and classes.
For classes, the following rules are observed:
If there is an initialization_list constructor of the appropriate type, that
constructor is used
Otherwise the class elements are initialized using the appropriate constructor

C++ 11 Part 1

Uniform initialization
class MyStruct
{
private:
int m_nX;
float m_nY;
public:
MyStruct(int x, float y): m_nX(x), m_nY(y) {};
};
MyStruct foo {2, 3.5f};

C++ 11 Part 1

Uniform initialization
initializer_list constructor takes precedence over other
constructors when doing uniform initialization.
std::vector<int> v1(8);
// creates an empty vector of size 8, using the int
constructor
std::vector<int> v1{8};
// creates a one-element vector with data value 8,
// using the initializer_list constructor

C++ 11 Part 1

Uniform initialization
You can also use the uniform initialization syntax when calling or return
values in functions
void useMyStruct(MyStruct x)
{
}
useMyStruct({2, 3.5f});
// use uniform initialization to create a MyStruct implicitly
MyStruct makeMyStruct(void)
{
return {2, 3.5f};
// use uniform initialization to create a MyStruct implicitly
}

C++ 11 Part 1

Type inference
In C++11, if the compiler can infer the type of a variable at the
point of declaration, instead of putting in the variable type,
you can just write auto
int x = 4;
can now be replaced with
auto x = 4;
vector<int> vec;
auto itr = vec.iterator();
// instead of vector<int>::iterator itr

C++ 11 Part 1

Type inference
See Example5.cpp
Apply type inference to simplify the code in both the template function and
the code in main()
template <typename BuiltType, typename Builder>
void makeAndProcessObject (const Builder& builder)
{
BuiltType val = builder.makeObject();
// do stuff with val
}
int main()
{
MyObjBuilder builder;
makeAndProcessObject<MyObj>( builder );
return 0;
}

C++ 11 Part 1

Type inference
See Solution5.cpp
Now you only need a single template parameter, and that parameter is easily
inferred when calling the function:
template <typename Builder>
void makeAndProcessObject (const Builder& builder)
{
auto val = builder.makeObject();
// do stuff with val
}
int main()
{
MyObjBuilder builder;
makeAndProcessObject( builder );
return 0;
}

C++ 11 Part 1

New Return Value Syntax


int multiply (int x, int y);
In C++11, you can now put the return value at the end of the
function declaration, substituting auto for the name of the
return type, if you want to
auto multiply (int x, int y) -> int;
See Example6.cpp
Identify the error
Provide a fix

C++ 11 Part 1

decltype
decltype lets you extract the type from a variable (or any
other expression)
int x = 3;
decltype(x) y = x; // same thing as auto y = x;
See Example7.cpp
Provide a clean solution

C++ 11 Part 1

Variadic function
Consider the following code to handle any number of arguments
#include <initializer_list>
int sum(std::initializer_list<int> numbers)
{
int total = 0;
for(auto i = numbers.begin(); i != numbers.end(); i++)
{
total += *i;
}
return total;
}
// later in code
sum( { 1, 2, 3, 4, 5 } ); // returns 15

C++ 11 Part 1

Variadic templates
Variadic templates can take variadic arguments.
int sum()
{
return 0;
}

// gets called when no arguments are passed

template<typename ... Types>


int sum (int first, Types ... rest)
{
return first + sum(rest...);
}
// later in code
sum(1, 2, 3, 4, 5); //returns 15

C++ 11 Part 1

Lambda functions and expressions


One of the most exciting features of C++11 is ability to create lambda
functions (sometimes referred to as closures).
A lambda function is a function that you can write inline in your source
code (usually to pass in to another function, similar to the idea of
a functor or function pointer).
With lambda, creating quick functions has become much easier
No need to write separate functions
int main()
{
auto func = [] () { cout << "Hello world"; };
func(); // now call the function
}

C++ 11 Part 1

Another example
class AddressBook
{
public:
template<typename Func>
std::vector<std::string> findMatchingAddresses (Func func) {
std::vector<std::string> results;
for ( auto itr = _addresses.begin(), end = _addresses.end(); itr != end; ++itr )
{
if ( func( *itr ) )
{
results.push_back( *itr );
}
}
return results;
}
private:
std::vector<std::string> _addresses;
};

C++ 11 Part 1

Another example
AddressBook global_address_book;
vector<string> findAddressesFromOrgs ()
{
return global_address_book.findMatchingAddresses(
// we're declaring a lambda here; the [] signals the start
[] (const string& addr) { return addr.find( ".org" ) != string::npos; }
);
}

C++ 11 Part 1

Variable Capture with lambdas


string name;
cin>> name;
return global_address_book.findMatchingAddresses(
// notice that the lambda function uses the the variable 'name'
[&] (const string& addr) { return addr.find( name ) != string::npos; }
);
int min_len = 0;
cin >> min_len;
return global_address_book.find( [&] (const string& addr) {
return addr.length() >= min_len; } );

C++ 11 Part 1

lambda options

[]
[&]
[=]
[=, &foo]

[bar]
[this]

Capture nothing (or, a scorched earth strategy?)


Capture any referenced variable by reference
Capture any referenced variable by making a copy
Capture any referenced variable by making a copy, but capture
variable foo by reference
Capture bar by making a copy; don't copy anything else
Capture the this pointer of the enclosing class

C++ 11 Part 1

Lambda and STL


vector<int> v;
v.push_back( 1 );
v.push_back( 2 );
//...
for ( auto itr = v.begin(), end = v.end(); itr != end; itr++ )
{
cout << *itr;
}
vector<int> v;
v.push_back( 1 );
v.push_back( 2 );
//...
for_each( v.begin(), v.end(), [] (int val) { cout << val; } );

C++ 11 Part 1

std::function

The new std::function is a great way of passing around lambda functions both as
parameters and as return values. It allows you to specify the exact types for the
argument list and the return value in the template.
class AddressBook {
public:
std::vector<string> findMatchingAddresses (std::function<bool (const string&)> func)
std::vector<string> results;
for ( auto itr = _addresses.begin(), end = _addresses.end(); itr != end; ++itr )
{
// call the function passed into findMatchingAddresses and see if it matches
if ( func( *itr ) )
{
results.push_back( *itr );
}
}
return results;
}
private:
std::vector<string> _addresses;
};

C++ 11 Part 1

Example Handling exceptions


vector<int> elements(3);
vector<int> indices(3);
indices[0] = 0;
indices[1] = -1; // This is not a subscript. It will trigger the exception.
indices[2] = 2;
// Use the values from the vector of index values to fill the elements vector.
// Use a try/catch block to handle invalid access to the elements vector.
try
{
for_each(indices.begin(), indices.end(),
[&] (int index) { elements.at(index) = index; });
}
catch (const out_of_range& e)
{
cerr << "Caught '" << e.what() << "'." << endl;
};

C++ 11 Part 1

Making delegates with lambda


When you call a normal function, all you need is the function
itself.
When you call a method on an object, you need two things:
the function and the object itself.
See Example8.cpp
Provide a clean solution by using a delegate with lambda

C++ 11 Part 1

Question Time

Please try to limit the questions to the topics discussed during the session.
Participants can clarify other doubts during the breaks.
Thank you.

Potrebbero piacerti anche