trainings/AdvancedCppV2/Presentation/moder_cpp_cpp17_constexpr.md

425 lines
12 KiB
Markdown

## constexpr - one of the most undervalued feature in modern C++ (1)
* <!-- .element: class="fragment fade-in" --> <code>constexpr</code> was introduced in C++11
* <!-- .element: class="fragment fade-in" --> constexpr values are know during compilation process
* <!-- .element: class="fragment fade-in" --> Function also could be constexpr with some restriction:
* <!-- .element: class="fragment fade-in" --> they may only have exactly one statement and this statement had to be a return statement
* <!-- .element: class="fragment fade-in" --> not allowed to have any side effects, like if-else
* <!-- .element: class="fragment fade-in" --> not allowed to have exception and try-catch block
* <!-- .element: class="fragment fade-in" --> can use ternary operator
* <!-- .element: class="fragment fade-in" --> Usually wrote recurent rather then iterative functions
* <!-- .element: class="fragment fade-in" --> Functions behave like normal functions (not solved during compilation) when working with non-constexpr arguments
* <!-- .element: class="fragment fade-in" --> Class/ struct could have constexpr C'tor and member functions
* <!-- .element: class="fragment fade-in" --> In C++14 introduced:
* <!-- .element: class="fragment fade-in" --> <b>Less restrictions for functions</b>:
* <!-- .element: class="fragment fade-in" --> more then one return
* <!-- .element: class="fragment fade-in" --> can use if-else
___
### constexpr - one of the most undervalued feature in modern C++ (2)
* <!-- .element: class="fragment fade-in" --> In C++17 introduced:
* <!-- .element: class="fragment fade-in" --> constexpr class string_view
* <!-- .element: class="fragment fade-in" --> <b>constexpr std::array</b>
* <!-- .element: class="fragment fade-in" --> constexpr iterator for array and std::begin, std::end functions
* <!-- .element: class="fragment fade-in" --> <b>constexpr lambda</b>
* <!-- .element: class="fragment fade-in" --> In C++20 introduced:
* <!-- .element: class="fragment fade-in" --> <b>constexpr STL algorithms</b>
* <!-- .element: class="fragment fade-in" --> <b>constexpr std::vector and std::string</b> and their iterators. (not supported by clang and gcc. Surprisely its supported by MSVC STL)
* <!-- .element: class="fragment fade-in" --> Allow to use try-catch in constexpr functions
* <!-- .element: class="fragment fade-in" --> Allow to allocate and deallocate values on heap, but need to free them before exit function
* <!-- .element: class="fragment fade-in" --> Allow to create virtual functions
* <!-- .element: class="fragment fade-in" --> Allow to use asm code block
___
## C++11 constexpr function
```C++
#include <cstddef>
constexpr bool isLower(char c) {
return c >= 'a' && c <= 'z';
}
template <typename T, size_t N>
constexpr size_t countLower(const T (&str)[N], size_t current, size_t counter) {
return current == N
? counter
: isLower(str[current])
? countLower(str, current + 1, counter + 1)
: countLower(str, current + 1, counter);
}
int main() {
static_assert(9 == countLower("Ala has a cat", 0, 0));
}
```
<!-- .element: class="fragment fade-in" -->
```Bash
xor eax,eax
ret
nop WORD PTR cs:[rax+rax*1+0x0]
nop DWORD PTR [rax]
```
<!-- .element: class="fragment fade-in" -->
<style>
div {text-align: center;}
</style>
<div>check on <a href="https://godbolt.org/">https://godbolt.org/</a></div>
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.75em" -->
___
## C++14 constexpr function
```C++
constexpr bool isLower(char c) {
return c >= 'a' && c <= 'z';
}
template <typename T, size_t N>
constexpr size_t countLower(const T (&str)[N]) {
size_t counter = 0;
for (auto c : str) {
if (isLower(c)) {
++counter;
}
}
return counter;
}
int main() {
static_assert(9 == countLower("Ala has a cat"));
}
```
<!-- .element: class="fragment fade-in" -->
```Bash
xor eax,eax
ret
nop WORD PTR cs:[rax+rax*1+0x0]
nop DWORD PTR [rax]
```
<!-- .element: class="fragment fade-in" -->
<style>
div {text-align: center;}
</style>
<div>check on <a href="https://godbolt.org/">https://godbolt.org/</a></div>
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.75em" -->
___
## C++17 constexpr function (1)
```C++
template <typename T, size_t N>
constexpr size_t countLower(const T (&str)[N]) {
constexpr auto isLower = [](char c) { return c >= 'a' && c <= 'z'; };
size_t counter = 0;
for (auto c : str) {
if (isLower(c)) {
++counter;
}
}
return counter;
}
int main() {
static_assert(9 == countLower("Ala has a cat"));
}
```
<!-- .element: class="fragment fade-in" -->
```Bash
xor eax,eax
ret
nop WORD PTR cs:[rax+rax*1+0x0]
nop DWORD PTR [rax]
```
<!-- .element: class="fragment fade-in" -->
<style>
div {text-align: center;}
</style>
<div>check on <a href="https://godbolt.org/">https://godbolt.org/</a></div>
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.75em" -->
___
## C++17 constexpr function (2)
```C++
constexpr size_t countLower(const std::string_view& str) {
constexpr auto isLower = [](char c) { return c >= 'a' && c <= 'z'; };
size_t counter = 0;
for (size_t i = 0 ; i < str.size() ; ++i) {
if (isLower(str[i])) {
++counter;
}
}
return counter;
}
int main() {
static_assert(9 == countLower("Ala has a cat"));
}
```
<!-- .element: class="fragment fade-in" -->
```Bash
xor eax,eax
ret
cs nop WORD PTR [rax+rax*1+0x0]
nop DWORD PTR [rax]
```
<!-- .element: class="fragment fade-in" -->
<style>
div {text-align: center;}
</style>
<div>check on <a href="https://godbolt.org/">https://godbolt.org/</a></div>
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.75em" -->
___
## C++20 constexpr function
```C++
constexpr size_t countLower(const std::string& str) {
constexpr auto isLower = [](char c) { return c >= 'a' && c <= 'z'; };
size_t counter = 0;
for (size_t i = 0 ; i < str.size() ; ++i) {
if (isLower(str[i])) {
++counter;
}
}
return counter;
}
int main() {
static_assert(9 == countLower(std::string("Ala has a cat")));
}
```
<style>
div {text-align: center;}
</style>
<div>check on <a href="https://en.cppreference.com/w/cpp/compiler_support">https://en.cppreference.com/w/cpp/compiler_support</a></div>
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.75em" -->
___
## C++20 constexpr function - algorithms
```C++
constexpr size_t countLower(const std::string& str) {
constexpr auto isLower = [](char c) { return c >= 'a' && c <= 'z'; };
return std::count_if(cbegin(str), cend(str), isLower);
}
int main() {
static_assert(9 == countLower(std::string("Ala has a cat")));
}
```
<style>
div {text-align: center;}
</style>
<div>check on <a href="https://en.cppreference.com/w/cpp/compiler_support">https://en.cppreference.com/w/cpp/compiler_support</a></div>
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.75em" -->
___
## C++20 constexpr - allocation on heap (1)
```C++
template <typename T, size_t N>
constexpr std::array<T, N> generatePseudoRandom(int from, int to) {
std::array<T, N> arr;
const int seed = ((N << 1) ^ (from << to) ^ to) ^ ((from + to) ^ (N << 3));
const size_t size = to - from;
auto tmp = new int[size];
std::iota(tmp, tmp + size, from);
for (int i = 0; i < N; ++i) {
arr[i] = tmp[(seed ^ i) % size];
}
delete[] tmp;
return arr;
}
int main() {
static_assert(generatePseudoRandom<int, 10>(10, 20).size() == 10);
}
```
<!-- .element: class="fragment fade-in" -->
```C++
Will print: 18 19 16 17 14 15 12 13 10 11
```
<!-- .element: class="fragment fade-in" -->
```Bash
xor eax,eax
ret
cs nop WORD PTR [rax+rax*1+0x0]
nop DWORD PTR [rax]
```
<!-- .element: class="fragment fade-in" -->
<style>
div {text-align: center;}
</style>
<div>check on <a href="https://godbolt.org/">https://godbolt.org/</a></div>
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.68em" -->
___
## C++20 constexpr - allocation on heap (1)
If we forget to add <code>delete[]</code>, compiler will detect it!
```C++
template <typename T, size_t N>
constexpr std::array<T, N> generatePseudoRandom(int from, int to) {
std::array<T, N> arr;
const int seed = ((N << 1) ^ (from << to) ^ to) ^ ((from + to) ^ (N << 3));
const size_t size = to - from;
auto tmp = new int[size];
std::iota(tmp, tmp + size, from);
for (int i = 0; i < N; ++i) {
arr[i] = tmp[(seed ^ i) % size];
}
//delete[] tmp;
return arr;
}
int main() {
static_assert(generatePseudoRandom<int, 10>(10, 20).size() == 10);
}
```
<!-- .element: class="fragment fade-in" -->
```C++
<source>: In function 'int main()':
<source>:22:64: error: non-constant condition for static assertion
22 | static_assert(generatePseudoRandom<int, 10>(10, 20).size() == 10);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~
<source>:10:16: error: '(generatePseudoRandom<int, 10>(10, 20).std::array<int, 10>::size() == 10)' is not a constant expression because allocated storage has not been deallocated
10 | auto tmp = new int[size];
| ^~~~~~~~~~~~~
Execution build compiler returned: 1
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.62em" -->
___
### contexpr-if (1)
* <!-- .element: class="fragment fade-in" --> Who uses SFINAE?
* <!-- .element: class="fragment fade-in" --> Who likes SFINAE?
```C++
namespace {
constexpr double epsilon = 0.000001;
}
template <class T>
constexpr std::enable_if_t<std::is_floating_point_v<T>, bool>
equal(T lhs, T rhs) {
return std::abs(lhs - rhs) < epsilon;
}
template <class T>
constexpr std::enable_if_t<!std::is_floating_point_v<T>, bool>
equal(T lhs, T rhs) {
return lhs == rhs;
}
int main() {
static_assert(equal(10, 10) == true);
static_assert(equal(10.123f, 10.123f) == true);
static_assert(equal(10.45678, 10.45678) == true);
}
```
<!-- .element: class="fragment fade-in" -->
```C++
xor eax,eax
ret
cs nop WORD PTR [rax+rax*1+0x0]
nop DWORD PTR [rax]
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.62em" -->
___
### contexpr-if (2)
```C++
template <typename T>
constexpr bool equal(T lhs, T rhs) {
constexpr double epsilon = 0.000001;
if constexpr (std::is_floating_point_v<T>) {
return std::abs(lhs - rhs) < epsilon;
} else {
return lhs == rhs;
}
}
int main() {
static_assert(equal(10, 10) == true);
static_assert(equal(10.123f, 10.123f) == true);
static_assert(equal(10.45678, 10.45678) == true);
}
```
<!-- .element: class="fragment fade-in" -->
```C++
xor eax,eax
ret
cs nop WORD PTR [rax+rax*1+0x0]
nop DWORD PTR [rax]
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.62em" -->
___
### C++20 concept
```C++
template <typename T>
requires std::is_floating_point_v<T>
constexpr bool equal(T lhs, T rhs) {
constexpr double epsilon = 0.000001;
return std::abs(lhs - rhs) < epsilon;
}
// Work only, when both types are equal!
constexpr bool equal(auto lhs, auto rhs) {
return lhs == rhs;
}
int main() {
static_assert(equal(10, 10) == true);
static_assert(equal(10.123f, 10.123f) == true);
static_assert(equal(10.45678, 10.45678) == true);
}
```
<!-- .element: class="fragment fade-in" -->
```C++
xor eax,eax
ret
cs nop WORD PTR [rax+rax*1+0x0]
nop DWORD PTR [rax]
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.62em" -->