trainings/AdvancedCppV2/Presentation/moder_cpp_cpp17_folding.md

280 lines
6.2 KiB
Markdown

## Fold expressions
* <!-- .element: class="fragment fade-in" --> Folding is a new way of handling argument package
* <!-- .element: class="fragment fade-in" --> It can be one or two arguments
* <!-- .element: class="fragment fade-in" --> If it is two arguments we distinguish between
* <!-- .element: class="fragment fade-in" --> left folding
* <!-- .element: class="fragment fade-in" --> right folding
___
### Fold expressions - adding values
```C++
template <typename... Args>
int add(Args... args) {
return (args + ...);
}
int main() {
std::cout << add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << '\n';
std::cout << add() << '\n'; // Not compile!
}
```
<!-- .element: class="fragment fade-in" -->
```C++
55
```
<!-- .element: class="fragment fade-in" -->
___
### Fold expressions - adding values
```C++
template <typename... Args>
int add(Args... args) {
return (args + ... + 0);
}
int main() {
std::cout << add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << '\n';
std::cout << add() << '\n'; // OK
}
```
<!-- .element: class="fragment fade-in" -->
```C++
55
0
```
<!-- .element: class="fragment fade-in" -->
___
### Fold expressions - subtracting values
```C++
template <typename... Args>
int subR(Args... args) {
return (args - ...);
}
template <typename... Args>
int subL(Args... args) {
return (... - args);
}
int main() {
// (1 - 2)
std::cout << subR(1, 2) << '\n'; // -1
// (1 - 2)
std::cout << subL(1, 2) << '\n'; // -1
// (1 - (2 - 3))
std::cout << subR(1, 2, 3) << '\n'; // 2
// ((1 - 2) - 3)
std::cout << subL(1, 2, 3) << '\n'; // -4
// (1 - (2 - (3 - 4)))
std::cout << subR(1, 2, 3, 4) << '\n'; // -2
// (((1 - 2) - 3) - 4)
std::cout << subL(1, 2, 3, 4) << '\n'; // -8
// (1 - (2 - (3 - (4 - 5))))
std::cout << subR(1, 2, 3, 4, 5) << '\n'; // 3
// ((((1 - 2) - 3) - 4) - 5)
std::cout << subL(1, 2, 3, 4, 5) << '\n'; // -13
}
```
<!-- .slide: style="font-size: 0.69em" -->
___
### Fold expressions - make code work without parameters
Find a problem with the below code.
<!-- .element: class="fragment fade-in" -->
```C++
template <typename... Args>
int subR(Args... args) {
return (args - ... - 0);
}
template <typename... Args>
int subL(Args... args) {
// What's wrong here???
return (0 - ... - args);
}
int main() {
// (1 - (2 - (3 - (4 - 5))))
std::cout << subR(1, 2, 3, 4, 5) << '\n';
// ((((1 - 2) - 3) - 4) - 5)
std::cout << subL(1, 2, 3, 4, 5) << '\n';
std::cout << subR() << '\n';
std::cout << subL() << '\n';
}
```
<!-- .element: class="fragment fade-in" -->
```C++
3 // (1 - (2 - (3 - (4 - (5 - 0))))
-15 // (((((0 - 1) - 2) - 3) - 4) - 5)
0
0
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.75em" -->
___
## How to demand minimum one variable
```C++
template<typename Value, typename... Values>
auto average(Value const& value, Values const&... values)
{
return (value + ... + values) / (1. + sizeof...(values));
}
int main() {
std::cout << average(1, 2, 3, 4) << '\n'; // print 2.5
std::cout << average() << '\n' // NOT COMPILE!
}
```
___
## Calling member functions
```C++
template<typename... Args>
void loggStateForAll(const Args&... args)
{
(..., args.loggState());
}
struct A {
void loggState() const {
std::cout << "StateA: good!\n";
}
};
struct B {
void loggState() const {
std::cout << "StateB: bad!\n";
}
};
int main() {
loggStateForAll(A{}, B{}, A{}, B{});
/* Will print
StateA: good!
StateB: bad!
StateA: good!
StateB: bad! */
}
```
<!-- .slide: style="font-size: 0.74em" -->
___
## Fold expressions - insert to container (1)
```C++
class Foo {
public:
Foo(int num)
: num_(num) {}
int num() const { return num_; }
private:
int num_;
};
template <typename... Args>
void emplaceAll(std::vector<Foo>& vec, Args... args) {
(vec.emplace_back(args), ...);
}
int main() {
std::vector<Foo> vec;
emplaceAll(vec, 1, 2, 3, 4, 5, 6, 7);
std::transform(cbegin(vec), cend(vec), std::ostream_iterator<int>(std::cout, " "),
[](const auto& foo) { return foo.num(); });
}
```
<!-- .element: class="fragment fade-in" -->
```C++
1 2 3 4 5 6 7
```
<!-- .element: class="fragment fade-in" -->
* <b>What with <code>(..., vec.emplace_back(args))</code>?</b>
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.73em" -->
___
### Fold expressions - insert to container (2)
* <!-- .element: class="fragment fade-in" --> There is no right/left type of folding for one type of argument
* <!-- .element: class="fragment fade-in" --> Unary right fold (fun(arg0) , (fun(arg1) , (fun(arg2) , ...)))
* <!-- .element: class="fragment fade-in" --> Unary left fold (((fun(arg0) , fun(arg1)) , fun(arg2)) , ...
* <!-- .element: class="fragment fade-in" --> Only for binary folding, we can have different behavior
___
### Fold expressions - logic operators
```C++
template <typename... Args>
bool emplaceAll(std::set<int>& set, Args... args) {
return (set.insert(args).second && ...);
}
int main() {
std::set<int> set;
emplaceAll(set, 1, 2, 3, 4, 5, 1, 6, 7);
std::copy(cbegin(set), cend(set), std::ostream_iterator<int>(std::cout, " "));
}
```
<!-- .element: class="fragment fade-in" -->
```C++
1 2 3 4 5
```
<!-- .element: class="fragment fade-in" -->
<b>For bot way:</b>
<code>return (set.insert(args).second && ...);</code>
<!-- .element: class="fragment fade-in" -->
<b>and</b>
<code>return (... && set.insert(args).second);</code>
<b>output will be the same</b>
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.90em" -->
___
### Fold expressions - cooperation with STL algorithms
```C++
template <typename... Args>
bool HasAll(const std::vector<int>& vec, Args... args) {
return ((std::find(cbegin(vec), cend(vec), args) != std::cend(vec)) && ...);
}
int main() {
std::vector<int> vec{1, 2, 3, 4, 1, 2, 3, 5, 6, 7, 4};
std::cout << std::boolalpha << HasAll(vec, 2, 4, 6) << '\n';
std::cout << std::boolalpha << HasAll(vec, 2, 4, 6, 8) << '\n';
}
```
<!-- .element: class="fragment fade-in" -->
```C++
true
false
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.8em" -->