256 lines
7.4 KiB
Markdown
256 lines
7.4 KiB
Markdown
## Cpp14
|
|
<!-- .slide: data-background="#ccc" -->
|
|
|
|
* <!-- .element: class="fragment fade-in" --> A quick reminder of lesser known features
|
|
* <!-- .element: class="fragment fade-in" --> decltype(auto)
|
|
* <!-- .element: class="fragment fade-in" --> Variable templates
|
|
* <!-- .element: class="fragment fade-in" --> Binary literals (Finaly!)
|
|
* <!-- .element: class="fragment fade-in" --> Digit separators
|
|
___
|
|
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
|
|
|
|
## `decltype`
|
|
|
|
**Rationale**: Deduction provided in contexts where auto is not allowed.
|
|
<!-- .element: class="fragment fade-in" -->
|
|
`decltype` allows a compiler to deduce the type of the variable or expression, eg. the returned type can be deduced from function parameters.
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
```cpp
|
|
std::map<std::string, float> collection;
|
|
|
|
decltype(collection) other; // other has type of collection
|
|
decltype(collection)::mapped_type value; // value is float
|
|
|
|
template <typename T1, typename T2>
|
|
auto add(T1 a, T2 b) -> decltype(a + b) // from C++14 decltype not necessary
|
|
{
|
|
return a + b;
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
|
|
|
|
It is useful in various scenarios:
|
|
|
|
```C++
|
|
auto compare = [](const auto &first, const auto &second) {
|
|
if (first.size() == second.size()) {
|
|
return first < second;
|
|
}
|
|
return first.size() < second.size();
|
|
};
|
|
|
|
std::map<std::string, int, decltype(compare)> map(compare);
|
|
map.emplace("C++20", 20);
|
|
map.emplace("C++1234", 1234);
|
|
map.emplace("Bababab", 12);
|
|
map.emplace("Abababa", 13);
|
|
|
|
for (const auto &[standard, number] : map) {
|
|
std::cout << "Standard: " << standard << " | number: " << number << '\n';
|
|
/* Output:
|
|
Standard: C++20 | number: 20
|
|
Standard: Abababa | number: 13
|
|
Standard: Bababab | number: 12
|
|
Standard: C++1234 | number: 1234 */
|
|
}
|
|
```
|
|
<!-- .slide: style="font-size: 0.90em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
|
|
|
|
Since C++20 we can use lambda expressions in unevaluated operands:
|
|
|
|
```C++
|
|
using SquareRoot = decltype([](const int val) {
|
|
return std::sqrt(val);
|
|
});
|
|
|
|
using Compare = decltype([](const auto &first, const auto &second) {
|
|
if (first.size() == second.size()) {
|
|
return first < second;
|
|
}
|
|
return first.size() < second.size();
|
|
});
|
|
|
|
int main() {
|
|
std::vector<int> vec(30);
|
|
std::iota(begin(vec), end(vec), 0);
|
|
std::transform(begin(vec), end(vec), begin(vec), SquareRoot{});
|
|
for (const auto& el : vec) {
|
|
std::cout << el << ' ';
|
|
}
|
|
// Output:
|
|
// 0 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 5 5 5 5 5
|
|
|
|
// Compare lambda will be constructed by default
|
|
std::map<std::string, int, Compare> map;
|
|
map.emplace("C++20", 20);
|
|
map.emplace("C++1234", 1234);
|
|
map.emplace("Bababab", 12);
|
|
map.emplace("Abababa", 13);
|
|
}
|
|
```
|
|
|
|
Closure types are not default constructible before C++20. In C++20 a closure type that has no capture is default constructible. That's why we can do a litle magic here :)
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.70em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
|
|
|
|
## `decltype(auto)`
|
|
|
|
`decltype(auto)` deduction mechanism preserves type modifiers (references, const, volatile).
|
|
<!-- .element: class="fragment fade-in" -->
|
|
`auto` deduction mechanism does not preserve type modifiers.
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
When you write generic code you want to be able to perfectly forward a return type without knowing whether you are dealing with a reference or a value.
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
```cpp
|
|
template<typename Fun, class... Args>
|
|
decltype(auto) Example(Fun fun, Args&&... args)
|
|
{
|
|
return fun(std::forward<Args>(args)...);
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
|
|
|
|
## Let's test it
|
|
|
|
We have the following class:
|
|
|
|
```C++
|
|
class Server {
|
|
public:
|
|
bool addRequest(const std::string& serviceId, const std::string& request) {
|
|
return requests_.emplace(serviceId, request).second;
|
|
}
|
|
|
|
std::string& getRequest(const std::string& serviceId) {
|
|
if (const auto it = requests_.find(serviceId) ; it != std::cend(requests_)) {
|
|
return it->second;
|
|
}
|
|
throw std::runtime_error("Invalid serviceId");
|
|
}
|
|
|
|
private:
|
|
std::map<std::string, std::string> requests_;
|
|
};
|
|
|
|
int main() {
|
|
Server server;
|
|
server.addRequest("SuperService", "Eat meat first!");
|
|
|
|
server.getRequest("SuperService") += " Leave the potatoes";
|
|
std::cout << server.getRequest("SuperService") << '\n';
|
|
// Eat meat first! Leave the potatoes
|
|
}
|
|
```
|
|
<!-- .slide: style="font-size: 0.74em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
|
|
|
|
Now let's use it with generic function, but without `decltype(auto)`
|
|
|
|
```C++
|
|
template<typename Fun, class... Args>
|
|
auto RunFun(Fun fun, Args&&... args)
|
|
{
|
|
return fun(std::forward<Args>(args)...);
|
|
}
|
|
|
|
int main() {
|
|
Server server;
|
|
server.addRequest("SuperService", "Eat meat first!");
|
|
|
|
// Write that we want to return std::string&
|
|
RunFun([&server](const auto& id) -> std::string& { return server.getRequest(id); },
|
|
"SuperService") += " Leave the potatoes";
|
|
std::cout << server.getRequest("SuperService") << '\n';
|
|
// Output: Eat meat first!
|
|
// Compiler didn't emit any warning!
|
|
}
|
|
```
|
|
<!-- .slide: style="font-size: 0.80em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
|
|
|
|
Now fix this with `decltype(auto)`
|
|
|
|
```C++
|
|
template<typename Fun, class... Args>
|
|
decltype(auto) RunFun(Fun fun, Args&&... args)
|
|
{
|
|
return fun(std::forward<Args>(args)...);
|
|
}
|
|
|
|
int main() {
|
|
Server server;
|
|
server.addRequest("SuperService", "Eat meat first!");
|
|
|
|
// Write that we want to return std::string&
|
|
RunFun([&server](const auto& id) -> std::string& { return server.getRequest(id); },
|
|
"SuperService") += " Leave the potatoes";
|
|
std::cout << server.getRequest("SuperService") << '\n';
|
|
// Output: Eat meat first! Leave the potatoes
|
|
}
|
|
```
|
|
|
|
Now we avoid misleading and don't waste time on debugging sessions!
|
|
<!-- .element: class="fragment fade-in" -->
|
|
<!-- .slide: style="font-size: 0.80em" -->
|
|
___
|
|
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
|
|
|
|
## Variable templates
|
|
|
|
```C++
|
|
template <typename T>
|
|
constexpr T pi = T(3.141592653589793238462643383);
|
|
|
|
// Usual specialization rules apply:
|
|
template <>
|
|
constexpr const char* pi<const char*> = "pi";
|
|
|
|
template <>
|
|
constexpr const int pi<const int> = 4;
|
|
|
|
int main() {
|
|
std::cout << pi<double> << '\n'; // 3.14159
|
|
std::cout << pi<const char*> << '\n'; // pi
|
|
std::cout << pi<int> << '\n'; // 3
|
|
std::cout << pi<const int> << '\n'; // 4
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
___
|
|
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
|
|
|
|
## Binary literals
|
|
|
|
```C++
|
|
int main() {
|
|
std::cout << 0b10101010 << '\n'; // 170
|
|
const int val = 0b1111;
|
|
std::cout << (val ^ 0b1010) << '\n'; // 5
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
## Digit separators
|
|
|
|
<!-- .element: class="fragment fade-in" -->
|
|
```C++
|
|
const int milion = 1'000'000;
|
|
const double val = 123'456'789'101.000;
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|