Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Introduction To C++ - Day 4

Download as pdf or txt
Download as pdf or txt
You are on page 1of 28

C++11 Standard

C++11 is an ISO C++ standard, published in 2011. To compile for this standard, add the -std=c++11 flag to
a command-line compilation string if compiling with g++ or clang++.
Automatic type deduction
The auto keyword can be used to deduce the type of the variable based on the variable’s initializer:
auto mychar = 'A'; // char
auto myint = 123 + 456; // int
auto mydouble = 456.789; // double
Range-based loops
The range-based loop allow us to iterate over the range, such as C++ standard-library containers. The
range-based for loop is of the following form: for (type element : container).
std::vector<int> v = { 10, 20, 40, 5, -20, 75 };
for (auto el : v)
{
std::cout << el << '\n';
}
To iterate over the actual elements, and not copies of elements, we use the reference type:
for (auto& el : v) { }
110 Copyright © Slobodan Dmitrovic
C++11 Standard
Initializer lists
Initializer lists, represented by braces { }, allow us to initialize objects in a uniform way.
int x{ 123 };
int y = { 456 };
double d{ 3.14 };
std::vector<int> v = { 1, 2, 3, 4, 5 };
List initialization also prevents narrowing conversions.
int x = { 123.45 }; // error, does not allowing narrowing
The type of the initializer list itself can be a specialization of the following type:
std::initializer_list<T>
The type is defined inside an <initializer_list> header file. Example:
#include <initializer_list>

int main()
{
auto il = { 1, 2, 3, 4, 5 }; // std::initializer_list<int>
}
111 Copyright © Slobodan Dmitrovic
C++11 Standard
Move semantics
C++ 11 standard introduces the move semantics for classes. We can move data from one object to
another using the rvalue reference types. The rvalue reference has the signature of some_type&&. To cast
an expression to an rvalue reference, we use the std::move function.
class MyClass
{
public:
MyClass(MyClass&& otherobject) // move constructor
: // we implement the move logic in the constructor initializer list
{

}
MyClass& operator=(MyClass&& otherobject) // move assignment operator
{
// we implement the move logic here
return *this;
}
};

112 Copyright © Slobodan Dmitrovic


C++11 Standard
Lambda expressions
Lambda expressions are anonymous function objects. They allow us to write short code snippets to be
used as a standard-library function predicate. Lambdas have a capture list, marked by [ ] where we can
capture local variables by reference or copy, parameter list with optional parameters marked with ( ), and a
lambda body, marked with { }. An empty lambda looks like [] () {};. Lambda used as a predicate to count
even numbers in a vector:
#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
auto counteven = std::count_if(std::begin(v), std::end(v),
[](int x) {return x % 2 == 0; }); // lambda expression
std::cout << "The number of even vector elements is: " << counteven;
}

113 Copyright © Slobodan Dmitrovic


C++11 Standard
The constexpr Specifier
The constexpr specifier says that the variable or a function can be evaluated during compile-time. If the
expression can not be evaluated during compile-time, the compiler emits an error:
int main()
{
constexpr int n = 123; //OK, 123 is a compile-time constant expression
constexpr double d = 456.78; //OK, 456.78 is a compile-time constant expression
constexpr double d2 = d; //OK, d is a constant expression
int x = 123;
constexpr int n2 = x; //compile-time error, the value of x is not known during
// compile-time
}

114 Copyright © Slobodan Dmitrovic


C++11 Standard
Scoped Enumerators
The scoped enumerators are enumerators that do not leak their names into the surrounding scope.
Scoped enums have the following signature:
enum class enum_name { value1, value2, value3 };
enum class MyEnum
{
myfirstvalue,
mysecondvalue,
mythirdvalue
};

int main()
{
MyEnum myenum = MyEnum::myfirstvalue;
}
We can specify the underlying type for a scoped enumerator:
enum class MyCharEnum : char { enum1, enum2, enum3 };

115 Copyright © Slobodan Dmitrovic


C++11 Standard
Smart Pointers - Unique Pointer
Unique pointer is a pointer that owns the object it points to, automatically destroys the object and
deallocates the memory once it goes out of scope. A unique pointer has the std::unique_ptr<type_name>
signature. Unique pointers are declared inside the <memory> header file.
#include <iostream>
#include <memory>

int main()
{
std::unique_ptr<int> p(new int{ 123 });
std::cout << *p;
} // p goes out of scope here, the memory gets deallocated, the object gets
// destroyed
A unique pointer can not be copied, only moved.

116 Copyright © Slobodan Dmitrovic


C++11 Standard
Smart Pointers - Shared Pointer
We can have multiple shared pointers pointing at an object. We say they have a shared ownership. When
the last of the shared pointers pointing at our object goes out of scope, the object gets destroyed and the
memory gets deallocated. A shared pointer has the following syntax: std::shared_ptr<type_name> and is
declared inside the <memory> header.
#include <iostream>
#include <memory>

int main()
{
std::shared_ptr<int> p1(new int{ 123 });
std::shared_ptr<int> p2 = p1; // a shared pointer can be copied
std::shared_ptr<int> p3 = p1;
} // when the last of shared pointers goes out of scope, the memory gets deallocated

A shared pointer can be copied. It can be initialized using a std::make_shared function:


auto p1 = std::make_shared<int>(123);

117 Copyright © Slobodan Dmitrovic


C++11 Standard
std::unordered_set
The std::unordered_set is a container that allows for constant time insertion, searching, and removal of
elements. This container is implemented as an array of buckets. The hash value of each element is
calculated, and the object is placed into an appropriate bucket based on the hash value. The container is
declared inside the <unordered_set> header.
#include <iostream>
#include <unordered_set>

int main()
{
std::unordered_set<int> myunorderedset = { 10, -30, 50, -15, 70, 100 };
for (auto el : myunorderedset)
{
std::cout << el << '\n';
}
}
The unordered set's elements can not be modified and the container does not hold duplicate values. The
values are not sorted but are unique.

118 Copyright © Slobodan Dmitrovic


C++11 Standard
std::unordered_map
The std::unordered_map is a container of unordered key-value pairs with unique keys. This container also
allows for fast insertion, searching, and removal of elements. The container's data is organized into
buckets depending on the element’s key hash value. An unordered map is declared inside the
<unordered_map> header.
#include <iostream>
#include <unordered_map>

int main()
{
std::unordered_map<char, int> myumap = { {'A', 10}, {'B', 20}, {'C', 40} };
for (auto el : myumap)
{
std::cout << el.first << ' ' << el.second << '\n';
}
}
To insert an element into a map, we can use the .insert() member function:
myumap.insert({ 'D', 10 });

119 Copyright © Slobodan Dmitrovic


C++11 Standard
std::tuple
While std::pair can hold only two values, the std::tuple wrapper can hold more than two values. To use
tuples, we need to include the <tuple> header. To access a certain tuple element, we use the
std::get<index_of_an_element>(tuple_name) function:
#include <iostream>
#include <tuple>

int main()
{
std::tuple<char, int, double> mytuple = { 'a', 123, 3.14 };
std::cout << "The first element is: " << std::get<0>(mytuple) << '\n';
std::cout << "The second element is: " << std::get<1>(mytuple) << '\n';
std::cout << "The third element is: " << std::get<2>(mytuple) << '\n';
}

120 Copyright © Slobodan Dmitrovic


C++11 Standard
static_assert
The static_assert directive checks a static (constexpr) condition during compile time. If the condition is
false, the directive fails the compilation and displays an error message.
int main()
{
constexpr int x = 123;
static_assert(x == 456, "The constexpr value is not 456.");
}

Here the static_assert checks if the value of x is equal to 456 during compile time. Since it is not, the
compilation will fail with a "The constexpr value is not 456." message.

121 Copyright © Slobodan Dmitrovic


C++11 Standard
Defaulted and deleted functions
To force the instantiation of a default, compiler-generated functions, we provide the =default specifier in its
declaration. The defaulted default constructor example:
#include <iostream>

class MyClass
{
public:
MyClass() = default; // defaulted member function
MyClass(const MyClass& other)
{
std::cout << "Copy constructor invoked.";
}
};
int main()
{
MyClass o; // Now OK, the defaulted default constructor is there
MyClass o2 = o; // Invoking the copy constructor
}
122 Copyright © Slobodan Dmitrovic
C++11 Standard
Defaulted and deleted functions
The =default specifier, when used on a member function can be looked at as: whatever the language
rules, we want this default member function to be there. We do not want it to be implicitly disabled.
Similarly, if we want to disable a member function from appearing, we use the =delete specifier. To disable
the copy constructor and copy assignment, we would write:
class MyClass
{
public:
MyClass()
{
std::cout << "Default constructor invoked.";
}
MyClass(const MyClass& other) = delete; // delete the copy constructor
MyClass& operator=(const MyClass& other) = delete; // delete the copy
// assignment operator
};

123 Copyright © Slobodan Dmitrovic


C++11 Standard
Type aliases
A type alias is a user-provided name for the existing type. If we want to use a different name for the
existing type, we write: using my_type_name = existing_type_name; Example:
#include <iostream>
#include <string>
#include <vector>

using MyInt = int;


using MyString = std::string;
using MyVector = std::vector<int>;
int main()
{
MyInt x = 123;
MyString s = "Hello World";
MyVector v = { 1, 2, 3, 4, 5 };
}

124 Copyright © Slobodan Dmitrovic


C++14 Standard
Introduction
C++14 is an ISO C++ standard published in 2014. It brings some additions to the language and the
standard library, but mainly complements and fixes the C++11 standard.
When we say we want to use the C++11 standard, what we want is the C++14 standard. To compile for the
C++14, add the -std=c++14 flag to a command-line compilation string if using g++ or clang compiler.
In Visual Studio, choose Project / Options / Configuration Properties / C/C++ / Language / C++ Language
Standard and choose C++14.

125 Copyright © Slobodan Dmitrovic


C++14 Standard
Binary literals
C++14 standard introduces the fourth kind of integral literals called binary literals. Using binary literals, we
can represent the value in its binary form. The literal has a 0b prefix, followed by a sequence of ones and
zeros representing a value. To represent the number 42 as a binary literal, we write:
int main()
{
int x = 0b101010;
}

Important to remember Values are values; they are some sequence of bits and bytes in memory. What
can be different is the value representation. There are decimal, hexadecimal, octal, and binary
representations of the value. These different forms of the same thing can be relevant to us humans.

126 Copyright © Slobodan Dmitrovic


C++14 Standard
Digits Separators
In C++14, we can separate digits using single quote characters to make it more readable:
int main()
{
int x = 100'000'000;
}
The compiler ignores the quotes. The separators are only here for our benefit, for example, to split a large
number into more readable sections.

127 Copyright © Slobodan Dmitrovic


C++14 Standard
Auto for Functions
We can deduce the function's type based on the function's return statement value:
auto myintfn() // integer
{
return 123;
}

auto mydoublefn() // double


{
return 3.14;
}

int main()
{
auto x = myintfn(); // int
auto d = mydoublefn(); // double
}

128 Copyright © Slobodan Dmitrovic


C++14 Standard
Generic Lambdas
We can use auto parameters in lambda functions now. The type of the parameter will be deduced from the
value supplied to a lambda function. This is also called a generic lambda:
#include <iostream>

int main()
{
auto mylambda = [](auto p) { std::cout << "Lambda parameter: " << p << '\n'; };
mylambda(123);
mylambda(3.14);
}

129 Copyright © Slobodan Dmitrovic


C++14 Standard
std::make_unique
C++14 introduces a std::make_unique function for creating unique pointers. It is declared inside a
<memory> header. Prefer this function to raw new operator when creating unique pointers:
#include <iostream>
#include <memory>
class MyClass
{
private:
int x;
double d;
public:
MyClass(int xx, double dd) : x{ xx }, d{ dd } {}
void printdata() { std::cout << "x: " << x << ", d: " << d; }
};

int main()
{
auto p = std::make_unique<MyClass>(123, 456.789);
p->printdata();
} 130 Copyright © Slobodan Dmitrovic
C++17 Standard
The C++17 standard introduces new language and library features and changes some of the language
rules.
Nested Namespaces
The C++17 standard allows us define nested namespaces using multiple namespace resolution operator.
Example:
namespace MyNameSpace1::MyNameSpace2::MyNameSpace3
{
// some code
}
Pre C++17:
namespace MyNameSpace1
{
namespace MyNameSpace2
{
namespace MyNameSpace3
{
// some code
}
} 131 Copyright © Slobodan Dmitrovic
}
C++17 Standard
Structured Bindings
Structured binding binds the variable names to elements of compile-time known expressions, such as
arrays or maps. If we want to have multiple variables taking values of expression elements, we use the
structured bindings. The syntax is:
auto [myvar1, myvar2, myvar3] = some_expression;
A simple example where we bind three variables to three array elements would be:
int main()
{
int arr[] = { 1, 2, 3 };
auto [myvar1, myvar2, myvar3] = arr; // myvar1 is a copy of arr[0], etc
auto& [myrefvar1, myrefvar2, myrefvar3] = arr; // myrefvar1 is arr[0], etc
}

132 Copyright © Slobodan Dmitrovic


C++17 Standard
std::filesystem
The std::filesystem library allows us to work with files, paths, and folders on our system. The library is
declared through a <filesystem> header. Paths can represent paths to files and paths to folders. To check
if a given folder exists, we use:
#include <iostream>
#include <filesystem>

int main()
{
std::filesystem::path folderpath = "C:\\MyFolder\\";
if (std::filesystem::exists(folderpath))
{
std::cout << "The path: " << folderpath << " exists.";
}
else
{
std::cout << "The path: " << folderpath << " does not exist.";
}
}
133 Copyright © Slobodan Dmitrovic
C++17 Standard
std::filesystem
Below are some useful utility functions inside the std::filesystem namespace:

– std::filesystem::create_directory for creating a directory


– std::filesystem::copy for copying files and directories
– std::filesystem::remove for removing a file or an empty folder
– std::filesystem::remove_all for removing folders and subfolders

134 Copyright © Slobodan Dmitrovic


C++17 Standard
std::string_view
The string_view is a non-owning view of a string or a substring. It is a reference to something that is
already there in the memory. It is implemented as a pointer to some character sequence plus the size of
that sequence. With this kind of structure, we can parse strings efficiently. The std::string_view is declared
inside the <string_view> header file. Examples of a full string view and a view of 5 characters:
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <string_view> #include <string_view>

int main() int main()


{ {
std::string s = "Hello World."; std::string s = "Hello World.";
std::string_view sw(s); std::string_view sw(s.c_str(), 5);
std::cout << sw; std::cout << sw;
} }

135 Copyright © Slobodan Dmitrovic


C++17 Standard
std::any
The std::any container can hold a single value of any type. This container is declared inside the header
file. Example:
#include <any>
int main()
{
std::any a = 345.678;
std::any b = true;
std::any c = 123;
}
To access the value of an std::any object in a safe manner, we cast it to a type of our choice using the
std::any_cast function:
std::any a = 123;
std::cout << "Any accessed as an integer: " << std::any_cast<int>(a) << '\n';
a = 456.789;
std::cout << "Any accessed as a double: " << std::any_cast<double>(a) << '\n';
a = true;
std::cout << "Any accessed as a boolean: " << std::any_cast<bool>(a) << '\n';
136 Copyright © Slobodan Dmitrovic
C++17 Standard
std::variant
C++17 introduces a new way of working with unions using the std::variant class template from a <variant>
header. This class template offers a type-safe way of storing and accessing a union. To declare a variant
using a std::variant and access individual members, we would write:
#include <iostream>
#include <variant>
int main()
{
std::variant<char, int, double> myvariant{ 'a' }; // variant now holds a char
std::cout << std::get<0>(myvariant) << '\n'; // obtain a data member by index
std::cout << std::get<char>(myvariant) << '\n'; // obtain a data member by type
myvariant = 1024; // variant now holds an int
std::cout << std::get<1>(myvariant) << '\n'; // by index
std::cout << std::get<int>(myvariant) << '\n'; // by type
myvariant = 123.456; // variant now holds a double
}

137 Copyright © Slobodan Dmitrovic

You might also like