Introduction To C++ - Day 4
Introduction To C++ - Day 4
Introduction To C++ - Day 4
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;
}
};
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;
}
int main()
{
MyEnum myenum = MyEnum::myfirstvalue;
}
We can specify the underlying type for a scoped enumerator:
enum class MyCharEnum : char { enum1, enum2, enum3 };
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.
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
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.
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 });
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';
}
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.
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
};
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.
int main()
{
auto x = myintfn(); // int
auto d = mydoublefn(); // double
}
int main()
{
auto mylambda = [](auto p) { std::cout << "Lambda parameter: " << p << '\n'; };
mylambda(123);
mylambda(3.14);
}
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
}
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: